aiobungie

A Pythonic async/await wrapper for interacting with the Bungie API.

Base client.

Example
import aiobungie

client = aiobungie.Client('YOUR_API_KEY')

# Search for Destiny2 users.
async def main() -> None:
    users = await client.search_users('Crit')

    # Iterate over the users and take the first 5 results.
    for user in users.take(5):
        print(f'{user.name} ({user.code})')

        # Iterate through the users memberships.
        for membership in user.memberships:
            print(membership.type, membership.id)

client.run(main()) # or asyncio.run(main())

Single RESTClient instance.

The difference between base client and the REST clients:

  • No Hight-Level concepts.
  • All returned data are pure JSON objects from the API.
  • No object creation.
Example
import aiobungie

async def main() -> None:
    # Using `async with` context manager to close the session properly.
    async with aiobungie.RESTClient("TOKEN") as rest:
        payload = await rest.fetch_player('Fate怒', 4275)

        for membership in payload:
            print(membership['membershipId'], membership['iconPath'])

import asyncio
asyncio.run(main())

REST client pool.

A REST client pool allows you to acquire multiple RESTClient instances that shares the same connection.

Example
import aiobungie
import asyncio

pool = aiobungie.RESTPool("token")

async def func1() -> None:
    async with pool.acquire() as instance:
        tokens = await instance.fetch_oauth2_tokens('code')
        pool.metadata['tokens'] = tokens

# Other instance may access the tokens from pool since its shared.

async def func2() -> None:
    async with pool.acquire() as instance:
        tokens = pool.metadata['tokens']
        tokens = await instance.refresh_access_token(tokens.refresh_token)

async def main() -> None:
    await asyncio.gather(func1(), func2())

asyncio.run(main())

Should you use the base client or the REST client? This returns to you. For an example if you're building a website.

You can use python as a REST API in the backend with the RESTClient since all returned object are JSON objects. Which gives you the freedom to deserialize it and implement your own logic in the front-end.

Or of you're building a Discord bot for an example or something simple. The base client is the way to go.

  1# MIT License
  2#
  3# Copyright (c) 2020 - Present nxtlo
  4#
  5# Permission is hereby granted, free of charge, to any person obtaining a copy
  6# of this software and associated documentation files (the "Software"), to deal
  7# in the Software without restriction, including without limitation the rights
  8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9# copies of the Software, and to permit persons to whom the Software is
 10# furnished to do so, subject to the following conditions:
 11#
 12# The above copyright notice and this permission notice shall be included in all
 13# copies or substantial portions of the Software.
 14#
 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 21# SOFTWARE.
 22
 23"""A Pythonic `async`/`await` wrapper for interacting with the Bungie API.
 24
 25Base client.
 26
 27Example
 28-------
 29```py
 30import aiobungie
 31
 32client = aiobungie.Client('YOUR_API_KEY')
 33
 34# Search for Destiny2 users.
 35async def main() -> None:
 36    users = await client.search_users('Crit')
 37
 38    # Iterate over the users and take the first 5 results.
 39    for user in users.take(5):
 40        print(f'{user.name} ({user.code})')
 41
 42        # Iterate through the users memberships.
 43        for membership in user.memberships:
 44            print(membership.type, membership.id)
 45
 46client.run(main()) # or asyncio.run(main())
 47```
 48
 49Single RESTClient instance.
 50
 51The difference between base client and the REST clients:
 52
 53* No Hight-Level concepts.
 54* All returned data are pure JSON objects from the API.
 55* No object creation.
 56
 57Example
 58-------
 59```py
 60import aiobungie
 61
 62async def main() -> None:
 63    # Using `async with` context manager to close the session properly.
 64    async with aiobungie.RESTClient("TOKEN") as rest:
 65        payload = await rest.fetch_player('Fate怒', 4275)
 66
 67        for membership in payload:
 68            print(membership['membershipId'], membership['iconPath'])
 69
 70import asyncio
 71asyncio.run(main())
 72```
 73
 74REST client pool.
 75
 76A REST client pool allows you to acquire multiple `RESTClient` instances that shares the same connection.
 77
 78Example
 79-------
 80```py
 81import aiobungie
 82import asyncio
 83
 84pool = aiobungie.RESTPool("token")
 85
 86async def func1() -> None:
 87    async with pool.acquire() as instance:
 88        tokens = await instance.fetch_oauth2_tokens('code')
 89        pool.metadata['tokens'] = tokens
 90
 91# Other instance may access the tokens from pool since its shared.
 92
 93async def func2() -> None:
 94    async with pool.acquire() as instance:
 95        tokens = pool.metadata['tokens']
 96        tokens = await instance.refresh_access_token(tokens.refresh_token)
 97
 98async def main() -> None:
 99    await asyncio.gather(func1(), func2())
100
101asyncio.run(main())
102```
103
104Should you use the base client or the REST client?
105This returns to you. For an example if you're building a website.
106
107You can use python as a REST API in the backend with the RESTClient since all returned object are JSON objects.
108Which gives you the freedom to deserialize it and implement your own logic in the front-end.
109
110Or of you're building a Discord bot for an example or something simple. The base client is the way to go.
111"""
112
113
114from __future__ import annotations
115
116from aiobungie import builders
117from aiobungie import crates
118from aiobungie import interfaces
119from aiobungie import traits
120from aiobungie import typedefs
121from aiobungie import url
122from aiobungie.client import Client
123from aiobungie.error import *
124from aiobungie.internal import iterators
125from aiobungie.internal.assets import Image
126from aiobungie.internal.enums import *
127from aiobungie.internal.factory import Factory
128from aiobungie.internal.iterators import *
129from aiobungie.rest import *
130from aiobungie.undefined import Undefined
131from aiobungie.undefined import UndefinedOr
132from aiobungie.undefined import UndefinedType
133
134from ._info import __about__
135from ._info import __author__
136from ._info import __docs__
137from ._info import __email__
138from ._info import __license__
139from ._info import __url__
140from ._info import __version__
141
142# Alias for crate for backwards compatibility.
143crate = crates
144
145# Activity enums
146from .crates.activity import Difficulty
147
148# Components enums
149from .crates.components import ComponentFields
150from .crates.components import ComponentPrivacy
151
152# Entity enums
153from .crates.entity import GatingScope
154from .crates.entity import ObjectiveUIStyle
155from .crates.entity import ValueUIStyle
156
157# Fireteam enums.
158from .crates.fireteams import FireteamActivity
159from .crates.fireteams import FireteamDate
160from .crates.fireteams import FireteamLanguage
161from .crates.fireteams import FireteamPlatform
162
163# Records enums
164from .crates.records import RecordState
165
166__all__ = [mod for mod in dir() if not mod.startswith("_")]  # type: ignore
@attrs.define(auto_exc=True)
class AiobungieError(builtins.RuntimeError):
57@attrs.define(auto_exc=True)
58class AiobungieError(RuntimeError):
59    """Base exception class that all other errors inherit from."""

Base exception class that all other errors inherit from.

AiobungieError()
2def __init__(self, ):
3    BaseException.__init__(self, )

Method generated by attrs for class AiobungieError.

Inherited Members
builtins.BaseException
with_traceback
args
@typing.final
class AmmoType(builtins.int, aiobungie.Enum):
646@typing.final
647class AmmoType(int, Enum):
648    """AN enum for Detyiny 2 ammo types."""
649
650    NONE = 0
651    PRIMARY = 1
652    SPECIAL = 2
653    HEAVY = 3

AN enum for Detyiny 2 ammo types.

NONE = <AmmoType.NONE: 0>
PRIMARY = <AmmoType.PRIMARY: 1>
SPECIAL = <AmmoType.SPECIAL: 2>
HEAVY = <AmmoType.HEAVY: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class BadRequest(aiobungie.HTTPError):
147@attrs.define(auto_exc=True)
148class BadRequest(HTTPError):
149    """Bad requests exceptions."""
150
151    url: typing.Optional[typedefs.StrOrURL]
152    """The URL/endpoint caused this error."""
153
154    body: typing.Any
155    """The response body."""
156
157    headers: multidict.CIMultiDictProxy[str]
158    """The response headers."""
159
160    http_status: http.HTTPStatus = attrs.field(default=http.HTTPStatus.BAD_REQUEST)

Bad requests exceptions.

BadRequest( message: str, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], http_status: http.HTTPStatus = <HTTPStatus.BAD_REQUEST: 400>)
2def __init__(self, message, url, body, headers, http_status=attr_dict['http_status'].default):
3    self.message = message
4    self.url = url
5    self.body = body
6    self.headers = headers
7    self.http_status = http_status
8    BaseException.__init__(self, self.message,self.url,self.body,self.headers,self.http_status)

Method generated by attrs for class BadRequest.

url: Union[str, yarl.URL, NoneType]

The URL/endpoint caused this error.

body: Any

The response body.

headers: multidict._multidict.CIMultiDictProxy[str]

The response headers.

http_status: http.HTTPStatus

The response status.

Inherited Members
HTTPError
message
builtins.BaseException
with_traceback
args
@typing.final
class ClanMemberType(builtins.int, aiobungie.Enum):
701@typing.final
702class ClanMemberType(int, Enum):
703    """An enum for bungie clan member types."""
704
705    NONE = 0
706    BEGINNER = 1
707    MEMBER = 2
708    ADMIN = 3
709    ACTING_FOUNDER = 4
710    FOUNDER = 5

An enum for bungie clan member types.

NONE = <ClanMemberType.NONE: 0>
BEGINNER = <ClanMemberType.BEGINNER: 1>
MEMBER = <ClanMemberType.MEMBER: 2>
ADMIN = <ClanMemberType.ADMIN: 3>
ACTING_FOUNDER = <ClanMemberType.ACTING_FOUNDER: 4>
FOUNDER = <ClanMemberType.FOUNDER: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Class(builtins.int, aiobungie.Enum):
477@typing.final
478class Class(int, Enum):
479    """An Enum for Destiny character classes."""
480
481    TITAN = 0
482    HUNTER = 1
483    WARLOCK = 2
484    UNKNOWN = 3

An Enum for Destiny character classes.

TITAN = <Class.TITAN: 0>
HUNTER = <Class.HUNTER: 1>
WARLOCK = <Class.WARLOCK: 2>
UNKNOWN = <Class.UNKNOWN: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Client(aiobungie.traits.ClientApp):
  61class Client(traits.ClientApp):
  62    """Standard Bungie API client application.
  63
  64    This client deserialize the REST JSON responses using `aiobungie.internal.factory.Factory`
  65    and returns `aiobungie.crates` Python object implementations of the responses.
  66
  67    A `aiobungie.RESTClient` REST client can also be used alone for low-level concepts.
  68
  69    Parameters
  70    -----------
  71    token: `str`
  72        Your Bungie's API key or Token from the developer's portal.
  73
  74    Other Parameters
  75    ----------------
  76    rest_client: `aiobungie.interfaces.RESTInterface | None`
  77        An optional rest client instance you can pass.
  78        If set to `None` then the client will use the default instance.
  79
  80    max_retries : `int`
  81        The max retries number to retry if the request hit a `5xx` status code.
  82    max_ratelimit_retries : `int`
  83        The max retries number to retry if the request hit a `429` status code. Defaults to `3`.
  84    client_secret : `str | None`
  85        An optional application client secret,
  86        This is only needed if you're fetching OAuth2 tokens with this client.
  87    client_id : `int | None`
  88        An optional application client id,
  89        This is only needed if you're fetching OAuth2 tokens with this client.
  90    """
  91
  92    __slots__ = ("_rest", "_factory", "_client_secret", "_client_id")
  93
  94    def __init__(
  95        self,
  96        token: str,
  97        /,
  98        client_secret: typing.Optional[str] = None,
  99        client_id: typing.Optional[int] = None,
 100        *,
 101        rest_client: typing.Optional[interfaces.RESTInterface] = None,
 102        max_retries: int = 4,
 103        max_ratelimit_retries: int = 3,
 104    ) -> None:
 105
 106        self._client_secret = client_secret
 107        self._client_id = client_id
 108
 109        self._rest = (
 110            rest_client
 111            if rest_client is not None
 112            else rest_.RESTClient(
 113                token,
 114                client_secret,
 115                client_id,
 116                max_retries=max_retries,
 117                max_ratelimit_retries=max_ratelimit_retries,
 118            )
 119        )
 120
 121        self._factory = factory_.Factory(self)
 122
 123    @property
 124    def factory(self) -> factory_.Factory:
 125        return self._factory
 126
 127    @property
 128    def rest(self) -> interfaces.RESTInterface:
 129        return self._rest
 130
 131    @property
 132    def request(self) -> Client:
 133        return copy.copy(self)
 134
 135    @property
 136    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
 137        return self._rest.metadata
 138
 139    def run(
 140        self, future: collections.Coroutine[typing.Any, None, None], debug: bool = False
 141    ) -> None:
 142        loop: typing.Final[asyncio.AbstractEventLoop] = helpers.get_or_make_loop()
 143        try:
 144            if not loop.is_running():
 145                loop.set_debug(debug)
 146                loop.run_until_complete(future)
 147
 148        except Exception as exc:
 149            raise RuntimeError(f"Failed to run {future.__qualname__}") from exc
 150
 151        except KeyboardInterrupt:
 152            _LOG.warn("Unexpected Keyboard interrupt. Exiting.")
 153            return
 154
 155        finally:
 156            if self._rest.is_alive:
 157                # Clean up sessions.
 158                loop.run_until_complete(self._rest.close())
 159
 160    # * User methods.
 161
 162    async def fetch_current_user_memberships(self, access_token: str, /) -> user.User:
 163        """Fetch and return a user object of the bungie net user associated with account.
 164
 165        .. warning::
 166            This method requires OAuth2 scope and a Bearer access token.
 167
 168        Parameters
 169        ----------
 170        access_token : `str`
 171            A valid Bearer access token for the authorization.
 172
 173        Returns
 174        -------
 175        `aiobungie.crates.user.User`
 176            A user object includes the Destiny memberships and Bungie.net user.
 177        """
 178        resp = await self.rest.fetch_current_user_memberships(access_token)
 179
 180        return self.factory.deserialize_user(resp)
 181
 182    async def fetch_bungie_user(self, id: int, /) -> user.BungieUser:
 183        """Fetch a Bungie user by their BungieNet id.
 184
 185        .. note::
 186            This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id`
 187            for other memberships.
 188
 189        Parameters
 190        ----------
 191        id: `int`
 192            The user id.
 193
 194        Returns
 195        -------
 196        `aiobungie.crates.user.BungieUser`
 197            A Bungie user.
 198
 199        Raises
 200        ------
 201        `aiobungie.error.NotFound`
 202            The user was not found.
 203        """
 204        payload = await self.rest.fetch_bungie_user(id)
 205
 206        return self.factory.deserialize_bungie_user(payload)
 207
 208    async def search_users(
 209        self, name: str, /
 210    ) -> iterators.FlatIterator[user.SearchableDestinyUser]:
 211        """Search for players and return all players that matches the same name.
 212
 213        Parameters
 214        ----------
 215        name : `buildins.str`
 216            The user name.
 217
 218        Returns
 219        -------
 220        `aiobungie.iterators.FlatIterator[aiobungie.crates.DestinyMembership]`
 221            A sequence of destiny memberships.
 222        """
 223        payload = await self.rest.search_users(name)
 224
 225        return iterators.FlatIterator(
 226            [
 227                self.factory.deserialize_searched_user(user)
 228                for user in payload["searchResults"]
 229            ]
 230        )
 231
 232    async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]:
 233        """Fetch all available user themes.
 234
 235        Returns
 236        -------
 237        `collections.Sequence[aiobungie.crates.user.UserThemes]`
 238            A sequence of user themes.
 239        """
 240        data = await self.rest.fetch_user_themes()
 241
 242        return self.factory.deserialize_user_themes(data)
 243
 244    async def fetch_hard_types(
 245        self,
 246        credential: int,
 247        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
 248        /,
 249    ) -> user.HardLinkedMembership:
 250        """Gets any hard linked membership given a credential.
 251        Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now.
 252        Cross Save aware.
 253
 254        Parameters
 255        ----------
 256        credential: `int`
 257            A valid SteamID64
 258        type: `aiobungie.CredentialType`
 259            The credential type. This must not be changed
 260            Since its only credential that works "currently"
 261
 262        Returns
 263        -------
 264        `aiobungie.crates.user.HardLinkedMembership`
 265            Information about the hard linked data.
 266        """
 267
 268        payload = await self.rest.fetch_hardlinked_credentials(credential, type)
 269
 270        return user.HardLinkedMembership(
 271            id=int(payload["membershipId"]),
 272            type=enums.MembershipType(payload["membershipType"]),
 273            cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]),
 274        )
 275
 276    async def fetch_membership_from_id(
 277        self,
 278        id: int,
 279        /,
 280        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 281    ) -> user.User:
 282        """Fetch Bungie user's memberships from their id.
 283
 284        Notes
 285        -----
 286        * This returns both BungieNet membership and a sequence of the player's DestinyMemberships
 287        Which includes Stadia, Xbox, Steam and PSN memberships if the player has them,
 288        see `aiobungie.crates.user.DestinyMembership` for more details.
 289        * If you only want the bungie user. Consider using `Client.fetch_user` method.
 290
 291        Parameters
 292        ----------
 293        id : `int`
 294            The user's id.
 295        type : `aiobungie.MembershipType`
 296            The user's membership type.
 297
 298        Returns
 299        -------
 300        `aiobungie.crates.User`
 301            A Bungie user with their membership types.
 302
 303        Raises
 304        ------
 305        aiobungie.NotFound
 306            The requested user was not found.
 307        """
 308        payload = await self.rest.fetch_membership_from_id(id, type)
 309
 310        return self.factory.deserialize_user(payload)
 311
 312    async def fetch_user_credentials(
 313        self, access_token: str, membership_id: int, /
 314    ) -> collections.Sequence[user.UserCredentials]:
 315        """Fetch an array of credential types attached to the requested account.
 316
 317        .. note::
 318            This method require OAuth2 Bearer access token.
 319
 320        Parameters
 321        ----------
 322        access_token : `str`
 323            The bearer access token associated with the bungie account.
 324        membership_id : `int`
 325            The id of the membership to return.
 326
 327        Returns
 328        -------
 329        `collections.Sequence[aiobungie.crates.UserCredentials]`
 330            A sequence of the attached user credentials.
 331
 332        Raises
 333        ------
 334        `aiobungie.Unauthorized`
 335            The access token was wrong or no access token passed.
 336        """
 337        resp = await self.rest.fetch_user_credentials(access_token, membership_id)
 338
 339        return self.factory.deserialize_user_credentials(resp)
 340
 341    # * Destiny 2.
 342
 343    async def fetch_profile(
 344        self,
 345        member_id: int,
 346        type: typedefs.IntAnd[enums.MembershipType],
 347        components: list[enums.ComponentType],
 348        auth: typing.Optional[str] = None,
 349    ) -> components.Component:
 350        """
 351        Fetch a bungie profile passing components to the request.
 352
 353        Parameters
 354        ----------
 355        member_id: `int`
 356            The member's id.
 357        type: `aiobungie.MembershipType`
 358            A valid membership type.
 359        components : `list[aiobungie.ComponentType]`
 360            List of profile components to collect and return.
 361
 362        Other Parameters
 363        ----------------
 364        auth : `typing.Optional[str]`
 365            A Bearer access_token to make the request with.
 366            This is optional and limited to components that only requires an Authorization token.
 367
 368        Returns
 369        --------
 370        `aiobungie.crates.Component`
 371            A Destiny 2 player profile with its components.
 372            Only passed components will be available if they exists. Otherwise they will be `None`
 373
 374        Raises
 375        ------
 376        `aiobungie.MembershipTypeError`
 377            The provided membership type was invalid.
 378        """
 379        data = await self.rest.fetch_profile(member_id, type, components, auth)
 380        return self.factory.deserialize_components(data)
 381
 382    async def fetch_linked_profiles(
 383        self,
 384        member_id: int,
 385        member_type: typedefs.IntAnd[enums.MembershipType],
 386        /,
 387        *,
 388        all: bool = False,
 389    ) -> profile.LinkedProfile:
 390        """Returns a summary information about all profiles linked to the requested member.
 391
 392        The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
 393
 394        .. note::
 395            It will only return linked accounts whose linkages you are allowed to view.
 396
 397        Parameters
 398        ----------
 399        member_id : `int`
 400            The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
 401        member_type : `aiobungie.MembershipType`
 402            The type for the membership whose linked Destiny account you want to return.
 403
 404        Other Parameters
 405        ----------------
 406        all : `bool`
 407            If provided and set to `True`, All memberships regardless
 408            of whether they're obscured by overrides will be returned,
 409
 410            If provided and set to `False`, Only available memberships will be returned.
 411            The default for this is `False`.
 412
 413        Returns
 414        -------
 415        `aiobungie.crates.profile.LinkedProfile`
 416            A linked profile object.
 417        """
 418        resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all)
 419
 420        return self.factory.deserialize_linked_profiles(resp)
 421
 422    async def fetch_player(
 423        self,
 424        name: str,
 425        code: int,
 426        /,
 427        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
 428    ) -> collections.Sequence[user.DestinyMembership]:
 429        """Fetch a Destiny 2 player's memberships.
 430
 431        Parameters
 432        -----------
 433        name: `str`
 434            The unique Bungie player name.
 435        code : `int`
 436            The unique Bungie display name code.
 437        type: `aiobungie.internal.enums.MembershipType`
 438            The player's membership type, e,g. XBOX, STEAM, PSN
 439
 440        Returns
 441        --------
 442        `collections.Sequence[aiobungie.crates.DestinyMembership]`
 443            A sequence of the found Destiny 2 player memberships.
 444            An empty sequence will be returned if no one found.
 445
 446        Raises
 447        ------
 448        `aiobungie.MembershipTypeError`
 449            The provided membership type was invalid.
 450        """
 451        resp = await self.rest.fetch_player(name, code, type)
 452
 453        return self.factory.deserialize_destiny_memberships(resp)
 454
 455    async def fetch_character(
 456        self,
 457        member_id: int,
 458        membership_type: typedefs.IntAnd[enums.MembershipType],
 459        character_id: int,
 460        components: list[enums.ComponentType],
 461        auth: typing.Optional[str] = None,
 462    ) -> components.CharacterComponent:
 463        """Fetch a Destiny 2 character.
 464
 465        Parameters
 466        ----------
 467        member_id: `int`
 468            A valid bungie member id.
 469        character_id: `int`
 470            The Destiny character id to retrieve.
 471        membership_type: `aiobungie.internal.enums.MembershipType`
 472            The member's membership type.
 473        components: `list[aiobungie.ComponentType]`
 474            Multiple arguments of character components to collect and return.
 475
 476        Other Parameters
 477        ----------------
 478        auth : `typing.Optional[str]`
 479            A Bearer access_token to make the request with.
 480            This is optional and limited to components that only requires an Authorization token.
 481
 482        Returns
 483        -------
 484        `aiobungie.crates.CharacterComponent`
 485            A Bungie character component.
 486
 487        `aiobungie.MembershipTypeError`
 488            The provided membership type was invalid.
 489        """
 490        resp = await self.rest.fetch_character(
 491            member_id, membership_type, character_id, components, auth
 492        )
 493
 494        return self.factory.deserialize_character_component(resp)
 495
 496    async def fetch_unique_weapon_history(
 497        self,
 498        membership_id: int,
 499        character_id: int,
 500        membership_type: typedefs.IntAnd[enums.MembershipType],
 501    ) -> collections.Sequence[activity.ExtendedWeaponValues]:
 502        """Fetch details about unique weapon usage for a character. Includes all exotics.
 503
 504        Parameters
 505        ----------
 506        membership_id : `int`
 507            The Destiny user membership id.
 508        character_id : `int`
 509            The character id to retrieve.
 510        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
 511            The Destiny user's membership type.
 512
 513        Returns
 514        -------
 515        `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]`
 516            A sequence of the weapon's extended values.
 517        """
 518        resp = await self._rest.fetch_unique_weapon_history(
 519            membership_id, character_id, membership_type
 520        )
 521
 522        return [
 523            self._factory.deserialize_extended_weapon_values(weapon)
 524            for weapon in resp["weapons"]
 525        ]
 526
 527    # * Destiny 2 Activities.
 528
 529    async def fetch_activities(
 530        self,
 531        member_id: int,
 532        character_id: int,
 533        mode: typedefs.IntAnd[enums.GameMode],
 534        *,
 535        membership_type: typedefs.IntAnd[
 536            enums.MembershipType
 537        ] = enums.MembershipType.ALL,
 538        page: int = 0,
 539        limit: int = 250,
 540    ) -> iterators.FlatIterator[activity.Activity]:
 541        """Fetch a Destiny 2 activity for the specified character id.
 542
 543        Parameters
 544        ----------
 545        member_id: `int`
 546            The user id that starts with `4611`.
 547        character_id: `int`
 548            The id of the character to retrieve the activities for.
 549        mode: `aiobungie.typedefs.IntAnd[aiobungie.internal.enums.GameMode]`
 550            This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
 551
 552        Other Parameters
 553        ----------------
 554        membership_type: `aiobungie.internal.enums.MembershipType`
 555            The Member ship type, if nothing was passed than it will return all.
 556        page: int
 557            The page number. Default is `0`
 558        limit: int
 559            Limit the returned result. Default is `250`.
 560
 561        Returns
 562        -------
 563        `aiobungie.iterators.FlatIterator[aiobungie.crates.Activity]`
 564            An iterator of the player's activities.
 565
 566        Raises
 567        ------
 568        `aiobungie.MembershipTypeError`
 569            The provided membership type was invalid.
 570        """
 571        resp = await self.rest.fetch_activities(
 572            member_id,
 573            character_id,
 574            mode,
 575            membership_type=membership_type,
 576            page=page,
 577            limit=limit,
 578        )
 579
 580        return self.factory.deserialize_activities(resp)
 581
 582    async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity:
 583        """Fetch a post activity details.
 584
 585        Parameters
 586        ----------
 587        instance_id: `int`
 588            The activity instance id.
 589
 590        Returns
 591        -------
 592        `aiobungie.crates.PostActivity`
 593           A post activity object.
 594        """
 595        resp = await self.rest.fetch_post_activity(instance_id)
 596
 597        return self.factory.deserialize_post_activity(resp)
 598
 599    async def fetch_aggregated_activity_stats(
 600        self,
 601        character_id: int,
 602        membership_id: int,
 603        membership_type: typedefs.IntAnd[enums.MembershipType],
 604    ) -> iterators.FlatIterator[activity.AggregatedActivity]:
 605        """Fetch aggregated activity stats for a character.
 606
 607        Parameters
 608        ----------
 609        character_id: `int`
 610            The id of the character to retrieve the activities for.
 611        membership_id: `int`
 612            The id of the user that started with `4611`.
 613        membership_type: `aiobungie.internal.enums.MembershipType`
 614            The Member ship type.
 615
 616        Returns
 617        -------
 618        `aiobungie.iterators.FlatIterator[aiobungie.crates.AggregatedActivity]`
 619            An iterator of the player's activities.
 620
 621        Raises
 622        ------
 623        `aiobungie.MembershipTypeError`
 624            The provided membership type was invalid.
 625        """
 626        resp = await self.rest.fetch_aggregated_activity_stats(
 627            character_id, membership_id, membership_type
 628        )
 629
 630        return self.factory.deserialize_aggregated_activities(resp)
 631
 632    # * Destiny 2 Clans or GroupsV2.
 633
 634    async def fetch_clan_from_id(
 635        self,
 636        id: int,
 637        /,
 638        access_token: typing.Optional[str] = None,
 639    ) -> clans.Clan:
 640        """Fetch a Bungie Clan by its id.
 641
 642        Parameters
 643        -----------
 644        id: `int`
 645            The clan id.
 646
 647        Returns
 648        --------
 649        `aiobungie.crates.Clan`
 650            An Bungie clan.
 651
 652        Raises
 653        ------
 654        `aiobungie.NotFound`
 655            The clan was not found.
 656        """
 657        resp = await self.rest.fetch_clan_from_id(id, access_token)
 658
 659        return self.factory.deserialize_clan(resp)
 660
 661    async def fetch_clan(
 662        self,
 663        name: str,
 664        /,
 665        access_token: typing.Optional[str] = None,
 666        *,
 667        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 668    ) -> clans.Clan:
 669        """Fetch a Clan by its name.
 670        This method will return the first clan found with given name.
 671
 672        Parameters
 673        ----------
 674        name: `str`
 675            The clan name
 676
 677        Other Parameters
 678        ----------------
 679        access_token : `typing.Optional[str]`
 680            An optional access token to make the request with.
 681
 682            If the token was bound to a member of the clan,
 683            This field `aiobungie.crates.Clan.current_user_membership` will be available
 684            and will return the membership of the user who made this request.
 685        type : `aiobungie.GroupType`
 686            The group type, Default is aiobungie.GroupType.CLAN.
 687
 688        Returns
 689        -------
 690        `aiobungie.crates.Clan`
 691            A Bungie clan.
 692
 693        Raises
 694        ------
 695        `aiobungie.NotFound`
 696            The clan was not found.
 697        """
 698        resp = await self.rest.fetch_clan(name, access_token, type=type)
 699
 700        return self.factory.deserialize_clan(resp)
 701
 702    async def fetch_clan_conversations(
 703        self, clan_id: int, /
 704    ) -> collections.Sequence[clans.ClanConversation]:
 705        """Fetch the conversations/chat channels of the given clan id.
 706
 707        Parameters
 708        ----------
 709        clan_id : `int`
 710            The clan id.
 711
 712        Returns
 713        `collections.Sequence[aiobungie.crates.ClanConversation]`
 714            A sequence of the clan chat channels.
 715        """
 716        resp = await self.rest.fetch_clan_conversations(clan_id)
 717
 718        return self.factory.deserialize_clan_conversations(resp)
 719
 720    async def fetch_clan_admins(
 721        self, clan_id: int, /
 722    ) -> iterators.FlatIterator[clans.ClanMember]:
 723        """Fetch the clan founder and admins.
 724
 725        Parameters
 726        ----------
 727        clan_id : `int`
 728            The clan id.
 729
 730        Returns
 731        -------
 732        `aiobungie.iterators.FlatIterator[aiobungie.crates.ClanMember]`
 733            An iterator over the found clan admins and founder.
 734
 735        Raises
 736        ------
 737        `aiobungie.NotFound`
 738            The requested clan was not found.
 739        """
 740        resp = await self.rest.fetch_clan_admins(clan_id)
 741
 742        return self.factory.deserialize_clan_members(resp)
 743
 744    async def fetch_groups_for_member(
 745        self,
 746        member_id: int,
 747        member_type: typedefs.IntAnd[enums.MembershipType],
 748        /,
 749        *,
 750        filter: int = 0,
 751        group_type: enums.GroupType = enums.GroupType.CLAN,
 752    ) -> collections.Sequence[clans.GroupMember]:
 753        """Fetch information about the groups that a given member has joined.
 754
 755        Parameters
 756        ----------
 757        member_id : `int`
 758            The member's id
 759        member_type : `aiobungie.MembershipType`
 760            The member's membership type.
 761
 762        Other Parameters
 763        ----------------
 764        filter : `int`
 765            Filter apply to list of joined groups. This Default to `0`
 766        group_type : `aiobungie.GroupType`
 767            The group's type.
 768            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
 769
 770        Returns
 771        -------
 772        `collections.Sequence[aiobungie.crates.GroupMember]`
 773            A sequence of joined groups for the fetched member.
 774        """
 775        resp = await self.rest.fetch_groups_for_member(
 776            member_id, member_type, filter=filter, group_type=group_type
 777        )
 778
 779        return [
 780            self.factory.deserialize_group_member(group) for group in resp["results"]
 781        ]
 782
 783    async def fetch_potential_groups_for_member(
 784        self,
 785        member_id: int,
 786        member_type: typedefs.IntAnd[enums.MembershipType],
 787        /,
 788        *,
 789        filter: int = 0,
 790        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 791    ) -> collections.Sequence[clans.GroupMember]:
 792        """Fetch the potential groups for a clan member.
 793
 794        Parameters
 795        ----------
 796        member_id : `int`
 797            The member's id
 798        member_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
 799            The member's membership type.
 800
 801        Other Parameters
 802        ----------------
 803        filter : `int`
 804            Filter apply to list of joined groups. This Default to `0`
 805        group_type : `aiobungie.typedefs.IntAnd[aiobungie.GroupType]`
 806            The group's type.
 807            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
 808
 809        Returns
 810        -------
 811        `collections.Sequence[aiobungie.crates.GroupMember]`
 812            A sequence of joined potential groups for the fetched member.
 813        """
 814        resp = await self.rest.fetch_potential_groups_for_member(
 815            member_id, member_type, filter=filter, group_type=group_type
 816        )
 817
 818        return [
 819            self.factory.deserialize_group_member(group) for group in resp["results"]
 820        ]
 821
 822    async def fetch_clan_members(
 823        self,
 824        clan_id: int,
 825        /,
 826        *,
 827        name: typing.Optional[str] = None,
 828        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 829    ) -> iterators.FlatIterator[clans.ClanMember]:
 830        """Fetch Bungie clan members.
 831
 832        Parameters
 833        ----------
 834        clan_id : `int`
 835            The clans id
 836
 837        Other Parameters
 838        ----------------
 839        name : `typing.Optional[str]`
 840            If provided, Only players matching this name will be returned.
 841        type : `aiobungie.MembershipType`
 842            An optional clan member's membership type.
 843            This parameter is used to filter the returned results
 844            by the provided membership, For an example XBox memberships only,
 845            Otherwise will return all memberships.
 846
 847        Returns
 848        -------
 849        `aiobungie.iterators.FlatIterator[aiobungie.crates.ClanMember]`
 850            An iterator over the bungie clan members.
 851
 852        Raises
 853        ------
 854        `aiobungie.NotFound`
 855            The clan was not found.
 856        """
 857        resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name)
 858
 859        return self.factory.deserialize_clan_members(resp)
 860
 861    async def fetch_clan_banners(self) -> collections.Sequence[clans.ClanBanner]:
 862        """Fetch the clan banners.
 863
 864        Returns
 865        -------
 866        `collections.Sequence[aiobungie.crates.ClanBanner]`
 867            A sequence of the clan banners.
 868        """
 869        resp = await self.rest.fetch_clan_banners()
 870
 871        return self.factory.deserialize_clan_banners(resp)
 872
 873    # This method is required to be here since it deserialize the clan.
 874    async def kick_clan_member(
 875        self,
 876        access_token: str,
 877        /,
 878        group_id: int,
 879        membership_id: int,
 880        membership_type: typedefs.IntAnd[enums.MembershipType],
 881    ) -> clans.Clan:
 882        """Kick a member from the clan.
 883
 884        .. note::
 885            This request requires OAuth2: oauth2: `AdminGroups` scope.
 886
 887        Parameters
 888        ----------
 889        access_token : `str`
 890            The bearer access token associated with the bungie account.
 891        group_id: `int`
 892            The group id.
 893        membership_id : `int`
 894            The member id to kick.
 895        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
 896            The member's membership type.
 897
 898        Returns
 899        -------
 900        `aiobungie.crates.clan.Clan`
 901            The clan that the member was kicked from.
 902        """
 903        resp = await self.rest.kick_clan_member(
 904            access_token,
 905            group_id=group_id,
 906            membership_id=membership_id,
 907            membership_type=membership_type,
 908        )
 909
 910        return self.factory.deserialize_clan(resp)
 911
 912    async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone:
 913        """Fetch a Bungie clan's weekly reward state.
 914
 915        Parameters
 916        ----------
 917        clan_id : `int`
 918            The clan's id.
 919
 920        Returns
 921        -------
 922        `aiobungie.crates.Milestone`
 923            A runtime status of the clan's milestone data.
 924        """
 925
 926        resp = await self.rest.fetch_clan_weekly_rewards(clan_id)
 927
 928        return self.factory.deserialize_milestone(resp)
 929
 930    # * Destiny 2 Entities aka Definitions.
 931
 932    async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity:
 933        """Fetch a static inventory item entity given a its hash.
 934
 935        Parameters
 936        ----------
 937        hash: `int`
 938            Inventory item's hash.
 939
 940        Returns
 941        -------
 942        `aiobungie.crates.InventoryEntity`
 943            A bungie inventory item.
 944        """
 945        resp = await self.rest.fetch_inventory_item(hash)
 946
 947        return self.factory.deserialize_inventory_entity(resp)
 948
 949    async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity:
 950        """Fetch a Destiny objective entity given a its hash.
 951
 952        Parameters
 953        ----------
 954        hash: `int`
 955            objective's hash.
 956
 957        Returns
 958        -------
 959        `aiobungie.crates.ObjectiveEntity`
 960            An objective entity item.
 961        """
 962        resp = await self.rest.fetch_objective_entity(hash)
 963
 964        return self.factory.deserialize_objective_entity(resp)
 965
 966    async def search_entities(
 967        self, name: str, entity_type: str, *, page: int = 0
 968    ) -> iterators.FlatIterator[entity.SearchableEntity]:
 969        """Search for Destiny2 entities given a name and its type.
 970
 971        Parameters
 972        ----------
 973        name : `str`
 974            The name of the entity, i.e., Thunderlord, One thousand voices.
 975        entity_type : `str`
 976            The type of the entity, AKA Definition,
 977            For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items.
 978
 979        Other Parameters
 980        ----------------
 981        page : `int`
 982            An optional page to return. Default to 0.
 983
 984        Returns
 985        -------
 986        `aiobungie.iterators.FlatIterator[aiobungie.crates.SearchableEntity]`
 987            An iterator over the found results matching the provided name.
 988        """
 989        resp = await self.rest.search_entities(name, entity_type, page=page)
 990
 991        return self.factory.deserialize_inventory_results(resp)
 992
 993    # Fireteams
 994
 995    async def fetch_fireteams(
 996        self,
 997        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
 998        *,
 999        platform: typedefs.IntAnd[
1000            fireteams.FireteamPlatform
1001        ] = fireteams.FireteamPlatform.ANY,
1002        language: typing.Union[
1003            fireteams.FireteamLanguage, str
1004        ] = fireteams.FireteamLanguage.ALL,
1005        date_range: int = 0,
1006        page: int = 0,
1007        slots_filter: int = 0,
1008    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1009        """Fetch public Bungie fireteams with open slots.
1010
1011        Parameters
1012        ----------
1013        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1014            The fireteam activity type.
1015
1016        Other Parameters
1017        ----------------
1018        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1019            If this is provided. Then the results will be filtered with the given platform.
1020            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1021        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1022            A locale language to filter the used language in that fireteam.
1023            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1024        date_range : `int`
1025            An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`.
1026        page : `int`
1027            The page number. By default its `0` which returns all available activities.
1028        slots_filter : `int`
1029            Filter the returned fireteams based on available slots. Default is `0`
1030
1031        Returns
1032        -------
1033        `typing.Optional[collections.Sequence[fireteams.Fireteam]]`
1034            A sequence of `aiobungie.crates.Fireteam` or `None`.
1035        """
1036
1037        resp = await self.rest.fetch_fireteams(
1038            activity_type,
1039            platform=platform,
1040            language=language,
1041            date_range=date_range,
1042            page=page,
1043            slots_filter=slots_filter,
1044        )
1045
1046        return self.factory.deserialize_fireteams(resp)
1047
1048    async def fetch_avaliable_clan_fireteams(
1049        self,
1050        access_token: str,
1051        group_id: int,
1052        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1053        *,
1054        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1055        language: typing.Union[fireteams.FireteamLanguage, str],
1056        date_range: int = 0,
1057        page: int = 0,
1058        public_only: bool = False,
1059        slots_filter: int = 0,
1060    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1061        """Fetch a clan's fireteams with open slots.
1062
1063        .. note::
1064            This method requires OAuth2: ReadGroups scope.
1065
1066        Parameters
1067        ----------
1068        access_token : `str`
1069            The bearer access token associated with the bungie account.
1070        group_id : `int`
1071            The group/clan id of the fireteam.
1072        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1073            The fireteam activity type.
1074
1075        Other Parameters
1076        ----------------
1077        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1078            If this is provided. Then the results will be filtered with the given platform.
1079            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1080        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1081            A locale language to filter the used language in that fireteam.
1082            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1083        date_range : `int`
1084            An integer to filter the date range of the returned fireteams. Defaults to `0`.
1085        page : `int`
1086            The page number. By default its `0` which returns all available activities.
1087        public_only: `bool`
1088            If set to True, Then only public fireteams will be returned.
1089        slots_filter : `int`
1090            Filter the returned fireteams based on available slots. Default is `0`
1091
1092        Returns
1093        -------
1094        `typing.Optional[collections.Sequence[aiobungie.crates.Fireteam]]`
1095            A sequence of  fireteams found in the clan.
1096            `None` will be returned if nothing was found.
1097        """
1098        resp = await self.rest.fetch_avaliable_clan_fireteams(
1099            access_token,
1100            group_id,
1101            activity_type,
1102            platform=platform,
1103            language=language,
1104            date_range=date_range,
1105            page=page,
1106            public_only=public_only,
1107            slots_filter=slots_filter,
1108        )
1109
1110        return self.factory.deserialize_fireteams(resp)
1111
1112    async def fetch_clan_fireteam(
1113        self, access_token: str, fireteam_id: int, group_id: int
1114    ) -> fireteams.AvailableFireteam:
1115        """Fetch a specific clan fireteam.
1116
1117        .. note::
1118            This method requires OAuth2: ReadGroups scope.
1119
1120        Parameters
1121        ----------
1122        access_token : `str`
1123            The bearer access token associated with the bungie account.
1124        group_id : `int`
1125            The group/clan id to fetch the fireteam from.
1126        fireteam_id : `int`
1127            The fireteam id to fetch.
1128
1129        Returns
1130        -------
1131        `typing.Optional[aiobungie.crates.AvailableFireteam]`
1132            A sequence of available fireteams objects if exists. else `None` will be returned.
1133        """
1134        resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id)
1135
1136        return self.factory.deserialize_available_fireteams(
1137            resp, no_results=True
1138        )  # type: ignore[return-value]
1139
1140    async def fetch_my_clan_fireteams(
1141        self,
1142        access_token: str,
1143        group_id: int,
1144        *,
1145        include_closed: bool = True,
1146        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1147        language: typing.Union[fireteams.FireteamLanguage, str],
1148        filtered: bool = True,
1149        page: int = 0,
1150    ) -> collections.Sequence[fireteams.AvailableFireteam]:
1151        """A method that's similar to `fetch_fireteams` but requires OAuth2.
1152
1153        .. note::
1154            This method requires OAuth2: ReadGroups scope.
1155
1156        Parameters
1157        ----------
1158        access_token : str
1159            The bearer access token associated with the bungie account.
1160        group_id : int
1161            The group/clan id to fetch.
1162
1163        Other Parameters
1164        ----------------
1165        include_closed : bool
1166            If provided and set to True, It will also return closed fireteams.
1167            If provided and set to False, It will only return public fireteams. Default is True.
1168        platform : aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]
1169            If this is provided. Then the results will be filtered with the given platform.
1170            Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
1171        language : typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]
1172            A locale language to filter the used language in that fireteam.
1173            Defaults to aiobungie.crates.FireteamLanguage.ALL
1174        filtered : bool
1175            If set to True, it will filter by clan. Otherwise not. Default is True.
1176        page : int
1177            The page number. By default its 0 which returns all available activities.
1178
1179        Returns
1180        -------
1181        `collections.Sequence[aiobungie.crates.AvailableFireteam]`
1182            A sequence of available fireteams objects if exists. else `None` will be returned.
1183        """
1184        resp = await self.rest.fetch_my_clan_fireteams(
1185            access_token,
1186            group_id,
1187            include_closed=include_closed,
1188            platform=platform,
1189            language=language,
1190            filtered=filtered,
1191            page=page,
1192        )
1193
1194        return self.factory.deserialize_available_fireteams(resp)  # type: ignore[return-value]
1195
1196    # Friends and social.
1197
1198    async def fetch_friends(
1199        self, access_token: str, /
1200    ) -> collections.Sequence[friends.Friend]:
1201        """Fetch bungie friend list.
1202
1203        .. note::
1204            This requests OAuth2: ReadUserData scope.
1205
1206        Parameters
1207        -----------
1208        access_token : `str`
1209            The bearer access token associated with the bungie account.
1210
1211        Returns
1212        -------
1213        `collections.Sequence[aiobungie.crates.Friend]`
1214            A sequence of the friends associated with that access token.
1215        """
1216
1217        resp = await self.rest.fetch_friends(access_token)
1218
1219        return self.factory.deserialize_friends(resp)
1220
1221    async def fetch_friend_requests(
1222        self, access_token: str, /
1223    ) -> friends.FriendRequestView:
1224        """Fetch pending bungie friend requests queue.
1225
1226        .. note::
1227            This requests OAuth2: ReadUserData scope.
1228
1229        Parameters
1230        -----------
1231        access_token : `str`
1232            The bearer access token associated with the bungie account.
1233
1234        Returns
1235        -------
1236        `aiobungie.crates.FriendRequestView`
1237            A friend requests view of that associated access token.
1238        """
1239
1240        resp = await self.rest.fetch_friend_requests(access_token)
1241
1242        return self.factory.deserialize_friend_requests(resp)
1243
1244    # Applications and Developer portal.
1245
1246    async def fetch_application(self, appid: int, /) -> application.Application:
1247        """Fetch a Bungie application.
1248
1249        Parameters
1250        -----------
1251        appid: `int`
1252            The application id.
1253
1254        Returns
1255        --------
1256        `aiobungie.crates.Application`
1257            A Bungie application.
1258        """
1259        resp = await self.rest.fetch_application(appid)
1260
1261        return self.factory.deserialize_app(resp)
1262
1263    # Milestones
1264
1265    async def fetch_public_milestone_content(
1266        self, milestone_hash: int, /
1267    ) -> milestones.MilestoneContent:
1268        """Fetch the milestone content given its hash.
1269
1270        Parameters
1271        ----------
1272        milestone_hash : `int`
1273            The milestone hash.
1274
1275        Returns
1276        -------
1277        `aiobungie.crates.milestones.MilestoneContent`
1278            A milestone content object.
1279        """
1280        resp = await self.rest.fetch_public_milestone_content(milestone_hash)
1281
1282        return self.factory.deserialize_public_milestone_content(resp)

Standard Bungie API client application.

This client deserialize the REST JSON responses using aiobungie.Factory and returns aiobungie.crates Python object implementations of the responses.

A aiobungie.RESTClient REST client can also be used alone for low-level concepts.

Parameters
  • token (str): Your Bungie's API key or Token from the developer's portal.
Other Parameters
  • rest_client (aiobungie.interfaces.RESTInterface | None): An optional rest client instance you can pass. If set to None then the client will use the default instance.
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • max_ratelimit_retries (int): The max retries number to retry if the request hit a 429 status code. Defaults to 3.
  • client_secret (str | None): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (int | None): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
Client( token: str, /, client_secret: Optional[str] = None, client_id: Optional[int] = None, *, rest_client: Optional[aiobungie.interfaces.rest.RESTInterface] = None, max_retries: int = 4, max_ratelimit_retries: int = 3)
 94    def __init__(
 95        self,
 96        token: str,
 97        /,
 98        client_secret: typing.Optional[str] = None,
 99        client_id: typing.Optional[int] = None,
100        *,
101        rest_client: typing.Optional[interfaces.RESTInterface] = None,
102        max_retries: int = 4,
103        max_ratelimit_retries: int = 3,
104    ) -> None:
105
106        self._client_secret = client_secret
107        self._client_id = client_id
108
109        self._rest = (
110            rest_client
111            if rest_client is not None
112            else rest_.RESTClient(
113                token,
114                client_secret,
115                client_id,
116                max_retries=max_retries,
117                max_ratelimit_retries=max_ratelimit_retries,
118            )
119        )
120
121        self._factory = factory_.Factory(self)

Returns the marshalling factory for the client.

rest: aiobungie.interfaces.rest.RESTInterface

Returns the REST client for the this client.

request: aiobungie.Client

A readonly ClientApp instance used for external requests.

metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

A mutable mapping storage for the user's needs.

def run( self, future: collections.abc.Coroutine[typing.Any, None, None], debug: bool = False) -> None:
139    def run(
140        self, future: collections.Coroutine[typing.Any, None, None], debug: bool = False
141    ) -> None:
142        loop: typing.Final[asyncio.AbstractEventLoop] = helpers.get_or_make_loop()
143        try:
144            if not loop.is_running():
145                loop.set_debug(debug)
146                loop.run_until_complete(future)
147
148        except Exception as exc:
149            raise RuntimeError(f"Failed to run {future.__qualname__}") from exc
150
151        except KeyboardInterrupt:
152            _LOG.warn("Unexpected Keyboard interrupt. Exiting.")
153            return
154
155        finally:
156            if self._rest.is_alive:
157                # Clean up sessions.
158                loop.run_until_complete(self._rest.close())

Runs a coroutine function until its complete.

This is equivalent to asyncio.get_event_loop().run_until_complete(...)

Parameters
  • future (collections.Coroutine[None, None, None]): A coroutine object.
  • debug (bool): Either to enable asyncio debug or not. Disabled by default.
Example
async def main() -> None:
    await fetch(...)

# Run the coroutine.
client.run(main())
async def fetch_current_user_memberships(self, access_token: str, /) -> aiobungie.crates.user.User:
162    async def fetch_current_user_memberships(self, access_token: str, /) -> user.User:
163        """Fetch and return a user object of the bungie net user associated with account.
164
165        .. warning::
166            This method requires OAuth2 scope and a Bearer access token.
167
168        Parameters
169        ----------
170        access_token : `str`
171            A valid Bearer access token for the authorization.
172
173        Returns
174        -------
175        `aiobungie.crates.user.User`
176            A user object includes the Destiny memberships and Bungie.net user.
177        """
178        resp = await self.rest.fetch_current_user_memberships(access_token)
179
180        return self.factory.deserialize_user(resp)

Fetch and return a user object of the bungie net user associated with account.

This method requires OAuth2 scope and a Bearer access token.

Parameters
  • access_token (str): A valid Bearer access token for the authorization.
Returns
  • aiobungie.crates.user.User: A user object includes the Destiny memberships and Bungie.net user.
async def fetch_bungie_user(self, id: int, /) -> aiobungie.crates.user.BungieUser:
182    async def fetch_bungie_user(self, id: int, /) -> user.BungieUser:
183        """Fetch a Bungie user by their BungieNet id.
184
185        .. note::
186            This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id`
187            for other memberships.
188
189        Parameters
190        ----------
191        id: `int`
192            The user id.
193
194        Returns
195        -------
196        `aiobungie.crates.user.BungieUser`
197            A Bungie user.
198
199        Raises
200        ------
201        `aiobungie.error.NotFound`
202            The user was not found.
203        """
204        payload = await self.rest.fetch_bungie_user(id)
205
206        return self.factory.deserialize_bungie_user(payload)

Fetch a Bungie user by their BungieNet id.

This returns a Bungie user membership only. Take a look at Client.fetch_membership_from_id for other memberships.

Parameters
  • id (int): The user id.
Returns
  • aiobungie.crates.user.BungieUser: A Bungie user.
Raises
async def search_users( self, name: str, /) -> aiobungie.FlatIterator[aiobungie.crates.user.SearchableDestinyUser]:
208    async def search_users(
209        self, name: str, /
210    ) -> iterators.FlatIterator[user.SearchableDestinyUser]:
211        """Search for players and return all players that matches the same name.
212
213        Parameters
214        ----------
215        name : `buildins.str`
216            The user name.
217
218        Returns
219        -------
220        `aiobungie.iterators.FlatIterator[aiobungie.crates.DestinyMembership]`
221            A sequence of destiny memberships.
222        """
223        payload = await self.rest.search_users(name)
224
225        return iterators.FlatIterator(
226            [
227                self.factory.deserialize_searched_user(user)
228                for user in payload["searchResults"]
229            ]
230        )

Search for players and return all players that matches the same name.

Parameters
  • name (buildins.str): The user name.
Returns
async def fetch_user_themes(self) -> collections.abc.Sequence[aiobungie.crates.user.UserThemes]:
232    async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]:
233        """Fetch all available user themes.
234
235        Returns
236        -------
237        `collections.Sequence[aiobungie.crates.user.UserThemes]`
238            A sequence of user themes.
239        """
240        data = await self.rest.fetch_user_themes()
241
242        return self.factory.deserialize_user_themes(data)

Fetch all available user themes.

Returns
  • collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of user themes.
async def fetch_hard_types( self, credential: int, type: Union[int, aiobungie.CredentialType] = <CredentialType.STEAMID: 12>, /) -> aiobungie.crates.user.HardLinkedMembership:
244    async def fetch_hard_types(
245        self,
246        credential: int,
247        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
248        /,
249    ) -> user.HardLinkedMembership:
250        """Gets any hard linked membership given a credential.
251        Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now.
252        Cross Save aware.
253
254        Parameters
255        ----------
256        credential: `int`
257            A valid SteamID64
258        type: `aiobungie.CredentialType`
259            The credential type. This must not be changed
260            Since its only credential that works "currently"
261
262        Returns
263        -------
264        `aiobungie.crates.user.HardLinkedMembership`
265            Information about the hard linked data.
266        """
267
268        payload = await self.rest.fetch_hardlinked_credentials(credential, type)
269
270        return user.HardLinkedMembership(
271            id=int(payload["membershipId"]),
272            type=enums.MembershipType(payload["membershipType"]),
273            cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]),
274        )

Gets any hard linked membership given a credential. Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now. Cross Save aware.

Parameters
  • credential (int): A valid SteamID64
  • type (aiobungie.CredentialType): The credential type. This must not be changed Since its only credential that works "currently"
Returns
  • aiobungie.crates.user.HardLinkedMembership: Information about the hard linked data.
async def fetch_membership_from_id( self, id: int, /, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>) -> aiobungie.crates.user.User:
276    async def fetch_membership_from_id(
277        self,
278        id: int,
279        /,
280        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
281    ) -> user.User:
282        """Fetch Bungie user's memberships from their id.
283
284        Notes
285        -----
286        * This returns both BungieNet membership and a sequence of the player's DestinyMemberships
287        Which includes Stadia, Xbox, Steam and PSN memberships if the player has them,
288        see `aiobungie.crates.user.DestinyMembership` for more details.
289        * If you only want the bungie user. Consider using `Client.fetch_user` method.
290
291        Parameters
292        ----------
293        id : `int`
294            The user's id.
295        type : `aiobungie.MembershipType`
296            The user's membership type.
297
298        Returns
299        -------
300        `aiobungie.crates.User`
301            A Bungie user with their membership types.
302
303        Raises
304        ------
305        aiobungie.NotFound
306            The requested user was not found.
307        """
308        payload = await self.rest.fetch_membership_from_id(id, type)
309
310        return self.factory.deserialize_user(payload)

Fetch Bungie user's memberships from their id.

Notes
  • This returns both BungieNet membership and a sequence of the player's DestinyMemberships Which includes Stadia, Xbox, Steam and PSN memberships if the player has them, see aiobungie.crates.user.DestinyMembership for more details.
  • If you only want the bungie user. Consider using Client.fetch_user method.
Parameters
Returns
Raises
async def fetch_user_credentials( self, access_token: str, membership_id: int, /) -> collections.abc.Sequence[aiobungie.crates.user.UserCredentials]:
312    async def fetch_user_credentials(
313        self, access_token: str, membership_id: int, /
314    ) -> collections.Sequence[user.UserCredentials]:
315        """Fetch an array of credential types attached to the requested account.
316
317        .. note::
318            This method require OAuth2 Bearer access token.
319
320        Parameters
321        ----------
322        access_token : `str`
323            The bearer access token associated with the bungie account.
324        membership_id : `int`
325            The id of the membership to return.
326
327        Returns
328        -------
329        `collections.Sequence[aiobungie.crates.UserCredentials]`
330            A sequence of the attached user credentials.
331
332        Raises
333        ------
334        `aiobungie.Unauthorized`
335            The access token was wrong or no access token passed.
336        """
337        resp = await self.rest.fetch_user_credentials(access_token, membership_id)
338
339        return self.factory.deserialize_user_credentials(resp)

Fetch an array of credential types attached to the requested account.

This method require OAuth2 Bearer access token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • membership_id (int): The id of the membership to return.
Returns
Raises
async def fetch_profile( self, member_id: int, type: Union[int, aiobungie.MembershipType], components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> aiobungie.crates.components.Component:
343    async def fetch_profile(
344        self,
345        member_id: int,
346        type: typedefs.IntAnd[enums.MembershipType],
347        components: list[enums.ComponentType],
348        auth: typing.Optional[str] = None,
349    ) -> components.Component:
350        """
351        Fetch a bungie profile passing components to the request.
352
353        Parameters
354        ----------
355        member_id: `int`
356            The member's id.
357        type: `aiobungie.MembershipType`
358            A valid membership type.
359        components : `list[aiobungie.ComponentType]`
360            List of profile components to collect and return.
361
362        Other Parameters
363        ----------------
364        auth : `typing.Optional[str]`
365            A Bearer access_token to make the request with.
366            This is optional and limited to components that only requires an Authorization token.
367
368        Returns
369        --------
370        `aiobungie.crates.Component`
371            A Destiny 2 player profile with its components.
372            Only passed components will be available if they exists. Otherwise they will be `None`
373
374        Raises
375        ------
376        `aiobungie.MembershipTypeError`
377            The provided membership type was invalid.
378        """
379        data = await self.rest.fetch_profile(member_id, type, components, auth)
380        return self.factory.deserialize_components(data)

Fetch a bungie profile passing components to the request.

Parameters
Other Parameters
  • auth (typing.Optional[str]): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
  • aiobungie.crates.Component: A Destiny 2 player profile with its components. Only passed components will be available if they exists. Otherwise they will be None
Raises
async def fetch_linked_profiles( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, all: bool = False) -> aiobungie.crates.profile.LinkedProfile:
382    async def fetch_linked_profiles(
383        self,
384        member_id: int,
385        member_type: typedefs.IntAnd[enums.MembershipType],
386        /,
387        *,
388        all: bool = False,
389    ) -> profile.LinkedProfile:
390        """Returns a summary information about all profiles linked to the requested member.
391
392        The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
393
394        .. note::
395            It will only return linked accounts whose linkages you are allowed to view.
396
397        Parameters
398        ----------
399        member_id : `int`
400            The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
401        member_type : `aiobungie.MembershipType`
402            The type for the membership whose linked Destiny account you want to return.
403
404        Other Parameters
405        ----------------
406        all : `bool`
407            If provided and set to `True`, All memberships regardless
408            of whether they're obscured by overrides will be returned,
409
410            If provided and set to `False`, Only available memberships will be returned.
411            The default for this is `False`.
412
413        Returns
414        -------
415        `aiobungie.crates.profile.LinkedProfile`
416            A linked profile object.
417        """
418        resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all)
419
420        return self.factory.deserialize_linked_profiles(resp)

Returns a summary information about all profiles linked to the requested member.

The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.

It will only return linked accounts whose linkages you are allowed to view.

Parameters
  • member_id (int): The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
  • member_type (aiobungie.MembershipType): The type for the membership whose linked Destiny account you want to return.
Other Parameters
  • all (bool): If provided and set to True, All memberships regardless of whether they're obscured by overrides will be returned,

    If provided and set to False, Only available memberships will be returned. The default for this is False.

Returns
  • aiobungie.crates.profile.LinkedProfile: A linked profile object.
async def fetch_player( self, name: str, code: int, /, type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>) -> collections.abc.Sequence[aiobungie.crates.user.DestinyMembership]:
422    async def fetch_player(
423        self,
424        name: str,
425        code: int,
426        /,
427        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
428    ) -> collections.Sequence[user.DestinyMembership]:
429        """Fetch a Destiny 2 player's memberships.
430
431        Parameters
432        -----------
433        name: `str`
434            The unique Bungie player name.
435        code : `int`
436            The unique Bungie display name code.
437        type: `aiobungie.internal.enums.MembershipType`
438            The player's membership type, e,g. XBOX, STEAM, PSN
439
440        Returns
441        --------
442        `collections.Sequence[aiobungie.crates.DestinyMembership]`
443            A sequence of the found Destiny 2 player memberships.
444            An empty sequence will be returned if no one found.
445
446        Raises
447        ------
448        `aiobungie.MembershipTypeError`
449            The provided membership type was invalid.
450        """
451        resp = await self.rest.fetch_player(name, code, type)
452
453        return self.factory.deserialize_destiny_memberships(resp)

Fetch a Destiny 2 player's memberships.

Parameters
  • name (str): The unique Bungie player name.
  • code (int): The unique Bungie display name code.
  • type (aiobungie.MembershipType): The player's membership type, e,g. XBOX, STEAM, PSN
Returns
Raises
async def fetch_character( self, member_id: int, membership_type: Union[int, aiobungie.MembershipType], character_id: int, components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> aiobungie.crates.components.CharacterComponent:
455    async def fetch_character(
456        self,
457        member_id: int,
458        membership_type: typedefs.IntAnd[enums.MembershipType],
459        character_id: int,
460        components: list[enums.ComponentType],
461        auth: typing.Optional[str] = None,
462    ) -> components.CharacterComponent:
463        """Fetch a Destiny 2 character.
464
465        Parameters
466        ----------
467        member_id: `int`
468            A valid bungie member id.
469        character_id: `int`
470            The Destiny character id to retrieve.
471        membership_type: `aiobungie.internal.enums.MembershipType`
472            The member's membership type.
473        components: `list[aiobungie.ComponentType]`
474            Multiple arguments of character components to collect and return.
475
476        Other Parameters
477        ----------------
478        auth : `typing.Optional[str]`
479            A Bearer access_token to make the request with.
480            This is optional and limited to components that only requires an Authorization token.
481
482        Returns
483        -------
484        `aiobungie.crates.CharacterComponent`
485            A Bungie character component.
486
487        `aiobungie.MembershipTypeError`
488            The provided membership type was invalid.
489        """
490        resp = await self.rest.fetch_character(
491            member_id, membership_type, character_id, components, auth
492        )
493
494        return self.factory.deserialize_character_component(resp)

Fetch a Destiny 2 character.

Parameters
  • member_id (int): A valid bungie member id.
  • character_id (int): The Destiny character id to retrieve.
  • membership_type (aiobungie.MembershipType): The member's membership type.
  • components (list[aiobungie.ComponentType]): Multiple arguments of character components to collect and return.
Other Parameters
  • auth (typing.Optional[str]): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
async def fetch_unique_weapon_history( self, membership_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> collections.abc.Sequence[aiobungie.crates.activity.ExtendedWeaponValues]:
496    async def fetch_unique_weapon_history(
497        self,
498        membership_id: int,
499        character_id: int,
500        membership_type: typedefs.IntAnd[enums.MembershipType],
501    ) -> collections.Sequence[activity.ExtendedWeaponValues]:
502        """Fetch details about unique weapon usage for a character. Includes all exotics.
503
504        Parameters
505        ----------
506        membership_id : `int`
507            The Destiny user membership id.
508        character_id : `int`
509            The character id to retrieve.
510        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
511            The Destiny user's membership type.
512
513        Returns
514        -------
515        `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]`
516            A sequence of the weapon's extended values.
517        """
518        resp = await self._rest.fetch_unique_weapon_history(
519            membership_id, character_id, membership_type
520        )
521
522        return [
523            self._factory.deserialize_extended_weapon_values(weapon)
524            for weapon in resp["weapons"]
525        ]

Fetch details about unique weapon usage for a character. Includes all exotics.

Parameters
Returns
async def fetch_activities( self, member_id: int, character_id: int, mode: Union[int, aiobungie.GameMode], *, membership_type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>, page: int = 0, limit: int = 250) -> aiobungie.FlatIterator[aiobungie.crates.activity.Activity]:
529    async def fetch_activities(
530        self,
531        member_id: int,
532        character_id: int,
533        mode: typedefs.IntAnd[enums.GameMode],
534        *,
535        membership_type: typedefs.IntAnd[
536            enums.MembershipType
537        ] = enums.MembershipType.ALL,
538        page: int = 0,
539        limit: int = 250,
540    ) -> iterators.FlatIterator[activity.Activity]:
541        """Fetch a Destiny 2 activity for the specified character id.
542
543        Parameters
544        ----------
545        member_id: `int`
546            The user id that starts with `4611`.
547        character_id: `int`
548            The id of the character to retrieve the activities for.
549        mode: `aiobungie.typedefs.IntAnd[aiobungie.internal.enums.GameMode]`
550            This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
551
552        Other Parameters
553        ----------------
554        membership_type: `aiobungie.internal.enums.MembershipType`
555            The Member ship type, if nothing was passed than it will return all.
556        page: int
557            The page number. Default is `0`
558        limit: int
559            Limit the returned result. Default is `250`.
560
561        Returns
562        -------
563        `aiobungie.iterators.FlatIterator[aiobungie.crates.Activity]`
564            An iterator of the player's activities.
565
566        Raises
567        ------
568        `aiobungie.MembershipTypeError`
569            The provided membership type was invalid.
570        """
571        resp = await self.rest.fetch_activities(
572            member_id,
573            character_id,
574            mode,
575            membership_type=membership_type,
576            page=page,
577            limit=limit,
578        )
579
580        return self.factory.deserialize_activities(resp)

Fetch a Destiny 2 activity for the specified character id.

Parameters
  • member_id (int): The user id that starts with 4611.
  • character_id (int): The id of the character to retrieve the activities for.
  • mode (aiobungie.typedefs.IntAnd[aiobungie.GameMode]): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
  • membership_type (aiobungie.MembershipType): The Member ship type, if nothing was passed than it will return all.
  • page (int): The page number. Default is 0
  • limit (int): Limit the returned result. Default is 250.
Returns
Raises
async def fetch_post_activity(self, instance_id: int, /) -> aiobungie.crates.activity.PostActivity:
582    async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity:
583        """Fetch a post activity details.
584
585        Parameters
586        ----------
587        instance_id: `int`
588            The activity instance id.
589
590        Returns
591        -------
592        `aiobungie.crates.PostActivity`
593           A post activity object.
594        """
595        resp = await self.rest.fetch_post_activity(instance_id)
596
597        return self.factory.deserialize_post_activity(resp)

Fetch a post activity details.

Parameters
  • instance_id (int): The activity instance id.
Returns
async def fetch_aggregated_activity_stats( self, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> aiobungie.FlatIterator[aiobungie.crates.activity.AggregatedActivity]:
599    async def fetch_aggregated_activity_stats(
600        self,
601        character_id: int,
602        membership_id: int,
603        membership_type: typedefs.IntAnd[enums.MembershipType],
604    ) -> iterators.FlatIterator[activity.AggregatedActivity]:
605        """Fetch aggregated activity stats for a character.
606
607        Parameters
608        ----------
609        character_id: `int`
610            The id of the character to retrieve the activities for.
611        membership_id: `int`
612            The id of the user that started with `4611`.
613        membership_type: `aiobungie.internal.enums.MembershipType`
614            The Member ship type.
615
616        Returns
617        -------
618        `aiobungie.iterators.FlatIterator[aiobungie.crates.AggregatedActivity]`
619            An iterator of the player's activities.
620
621        Raises
622        ------
623        `aiobungie.MembershipTypeError`
624            The provided membership type was invalid.
625        """
626        resp = await self.rest.fetch_aggregated_activity_stats(
627            character_id, membership_id, membership_type
628        )
629
630        return self.factory.deserialize_aggregated_activities(resp)

Fetch aggregated activity stats for a character.

Parameters
  • character_id (int): The id of the character to retrieve the activities for.
  • membership_id (int): The id of the user that started with 4611.
  • membership_type (aiobungie.MembershipType): The Member ship type.
Returns
Raises
async def fetch_clan_from_id( self, id: int, /, access_token: Optional[str] = None) -> aiobungie.crates.clans.Clan:
634    async def fetch_clan_from_id(
635        self,
636        id: int,
637        /,
638        access_token: typing.Optional[str] = None,
639    ) -> clans.Clan:
640        """Fetch a Bungie Clan by its id.
641
642        Parameters
643        -----------
644        id: `int`
645            The clan id.
646
647        Returns
648        --------
649        `aiobungie.crates.Clan`
650            An Bungie clan.
651
652        Raises
653        ------
654        `aiobungie.NotFound`
655            The clan was not found.
656        """
657        resp = await self.rest.fetch_clan_from_id(id, access_token)
658
659        return self.factory.deserialize_clan(resp)

Fetch a Bungie Clan by its id.

Parameters
  • id (int): The clan id.
Returns
Raises
async def fetch_clan( self, name: str, /, access_token: Optional[str] = None, *, type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> aiobungie.crates.clans.Clan:
661    async def fetch_clan(
662        self,
663        name: str,
664        /,
665        access_token: typing.Optional[str] = None,
666        *,
667        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
668    ) -> clans.Clan:
669        """Fetch a Clan by its name.
670        This method will return the first clan found with given name.
671
672        Parameters
673        ----------
674        name: `str`
675            The clan name
676
677        Other Parameters
678        ----------------
679        access_token : `typing.Optional[str]`
680            An optional access token to make the request with.
681
682            If the token was bound to a member of the clan,
683            This field `aiobungie.crates.Clan.current_user_membership` will be available
684            and will return the membership of the user who made this request.
685        type : `aiobungie.GroupType`
686            The group type, Default is aiobungie.GroupType.CLAN.
687
688        Returns
689        -------
690        `aiobungie.crates.Clan`
691            A Bungie clan.
692
693        Raises
694        ------
695        `aiobungie.NotFound`
696            The clan was not found.
697        """
698        resp = await self.rest.fetch_clan(name, access_token, type=type)
699
700        return self.factory.deserialize_clan(resp)

Fetch a Clan by its name. This method will return the first clan found with given name.

Parameters
  • name (str): The clan name
Other Parameters
Returns
Raises
async def fetch_clan_conversations( self, clan_id: int, /) -> collections.abc.Sequence[aiobungie.crates.clans.ClanConversation]:
702    async def fetch_clan_conversations(
703        self, clan_id: int, /
704    ) -> collections.Sequence[clans.ClanConversation]:
705        """Fetch the conversations/chat channels of the given clan id.
706
707        Parameters
708        ----------
709        clan_id : `int`
710            The clan id.
711
712        Returns
713        `collections.Sequence[aiobungie.crates.ClanConversation]`
714            A sequence of the clan chat channels.
715        """
716        resp = await self.rest.fetch_clan_conversations(clan_id)
717
718        return self.factory.deserialize_clan_conversations(resp)

Fetch the conversations/chat channels of the given clan id.

Parameters
async def fetch_clan_admins( self, clan_id: int, /) -> aiobungie.FlatIterator[aiobungie.crates.clans.ClanMember]:
720    async def fetch_clan_admins(
721        self, clan_id: int, /
722    ) -> iterators.FlatIterator[clans.ClanMember]:
723        """Fetch the clan founder and admins.
724
725        Parameters
726        ----------
727        clan_id : `int`
728            The clan id.
729
730        Returns
731        -------
732        `aiobungie.iterators.FlatIterator[aiobungie.crates.ClanMember]`
733            An iterator over the found clan admins and founder.
734
735        Raises
736        ------
737        `aiobungie.NotFound`
738            The requested clan was not found.
739        """
740        resp = await self.rest.fetch_clan_admins(clan_id)
741
742        return self.factory.deserialize_clan_members(resp)

Fetch the clan founder and admins.

Parameters
  • clan_id (int): The clan id.
Returns
Raises
async def fetch_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: aiobungie.GroupType = <GroupType.CLAN: 1>) -> collections.abc.Sequence[aiobungie.crates.clans.GroupMember]:
744    async def fetch_groups_for_member(
745        self,
746        member_id: int,
747        member_type: typedefs.IntAnd[enums.MembershipType],
748        /,
749        *,
750        filter: int = 0,
751        group_type: enums.GroupType = enums.GroupType.CLAN,
752    ) -> collections.Sequence[clans.GroupMember]:
753        """Fetch information about the groups that a given member has joined.
754
755        Parameters
756        ----------
757        member_id : `int`
758            The member's id
759        member_type : `aiobungie.MembershipType`
760            The member's membership type.
761
762        Other Parameters
763        ----------------
764        filter : `int`
765            Filter apply to list of joined groups. This Default to `0`
766        group_type : `aiobungie.GroupType`
767            The group's type.
768            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
769
770        Returns
771        -------
772        `collections.Sequence[aiobungie.crates.GroupMember]`
773            A sequence of joined groups for the fetched member.
774        """
775        resp = await self.rest.fetch_groups_for_member(
776            member_id, member_type, filter=filter, group_type=group_type
777        )
778
779        return [
780            self.factory.deserialize_group_member(group) for group in resp["results"]
781        ]

Fetch information about the groups that a given member has joined.

Parameters
Other Parameters
Returns
async def fetch_potential_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> collections.abc.Sequence[aiobungie.crates.clans.GroupMember]:
783    async def fetch_potential_groups_for_member(
784        self,
785        member_id: int,
786        member_type: typedefs.IntAnd[enums.MembershipType],
787        /,
788        *,
789        filter: int = 0,
790        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
791    ) -> collections.Sequence[clans.GroupMember]:
792        """Fetch the potential groups for a clan member.
793
794        Parameters
795        ----------
796        member_id : `int`
797            The member's id
798        member_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
799            The member's membership type.
800
801        Other Parameters
802        ----------------
803        filter : `int`
804            Filter apply to list of joined groups. This Default to `0`
805        group_type : `aiobungie.typedefs.IntAnd[aiobungie.GroupType]`
806            The group's type.
807            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
808
809        Returns
810        -------
811        `collections.Sequence[aiobungie.crates.GroupMember]`
812            A sequence of joined potential groups for the fetched member.
813        """
814        resp = await self.rest.fetch_potential_groups_for_member(
815            member_id, member_type, filter=filter, group_type=group_type
816        )
817
818        return [
819            self.factory.deserialize_group_member(group) for group in resp["results"]
820        ]

Fetch the potential groups for a clan member.

Parameters
Other Parameters
Returns
async def fetch_clan_members( self, clan_id: int, /, *, name: Optional[str] = None, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>) -> aiobungie.FlatIterator[aiobungie.crates.clans.ClanMember]:
822    async def fetch_clan_members(
823        self,
824        clan_id: int,
825        /,
826        *,
827        name: typing.Optional[str] = None,
828        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
829    ) -> iterators.FlatIterator[clans.ClanMember]:
830        """Fetch Bungie clan members.
831
832        Parameters
833        ----------
834        clan_id : `int`
835            The clans id
836
837        Other Parameters
838        ----------------
839        name : `typing.Optional[str]`
840            If provided, Only players matching this name will be returned.
841        type : `aiobungie.MembershipType`
842            An optional clan member's membership type.
843            This parameter is used to filter the returned results
844            by the provided membership, For an example XBox memberships only,
845            Otherwise will return all memberships.
846
847        Returns
848        -------
849        `aiobungie.iterators.FlatIterator[aiobungie.crates.ClanMember]`
850            An iterator over the bungie clan members.
851
852        Raises
853        ------
854        `aiobungie.NotFound`
855            The clan was not found.
856        """
857        resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name)
858
859        return self.factory.deserialize_clan_members(resp)

Fetch Bungie clan members.

Parameters
  • clan_id (int): The clans id
Other Parameters
  • name (typing.Optional[str]): If provided, Only players matching this name will be returned.
  • type (aiobungie.MembershipType): An optional clan member's membership type. This parameter is used to filter the returned results by the provided membership, For an example XBox memberships only, Otherwise will return all memberships.
Returns
Raises
async def fetch_clan_banners(self) -> collections.abc.Sequence[aiobungie.crates.clans.ClanBanner]:
861    async def fetch_clan_banners(self) -> collections.Sequence[clans.ClanBanner]:
862        """Fetch the clan banners.
863
864        Returns
865        -------
866        `collections.Sequence[aiobungie.crates.ClanBanner]`
867            A sequence of the clan banners.
868        """
869        resp = await self.rest.fetch_clan_banners()
870
871        return self.factory.deserialize_clan_banners(resp)

Fetch the clan banners.

Returns
async def kick_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> aiobungie.crates.clans.Clan:
874    async def kick_clan_member(
875        self,
876        access_token: str,
877        /,
878        group_id: int,
879        membership_id: int,
880        membership_type: typedefs.IntAnd[enums.MembershipType],
881    ) -> clans.Clan:
882        """Kick a member from the clan.
883
884        .. note::
885            This request requires OAuth2: oauth2: `AdminGroups` scope.
886
887        Parameters
888        ----------
889        access_token : `str`
890            The bearer access token associated with the bungie account.
891        group_id: `int`
892            The group id.
893        membership_id : `int`
894            The member id to kick.
895        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
896            The member's membership type.
897
898        Returns
899        -------
900        `aiobungie.crates.clan.Clan`
901            The clan that the member was kicked from.
902        """
903        resp = await self.rest.kick_clan_member(
904            access_token,
905            group_id=group_id,
906            membership_id=membership_id,
907            membership_type=membership_type,
908        )
909
910        return self.factory.deserialize_clan(resp)

Kick a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to kick.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
Returns
  • aiobungie.crates.clan.Clan: The clan that the member was kicked from.
async def fetch_clan_weekly_rewards(self, clan_id: int) -> aiobungie.crates.milestones.Milestone:
912    async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone:
913        """Fetch a Bungie clan's weekly reward state.
914
915        Parameters
916        ----------
917        clan_id : `int`
918            The clan's id.
919
920        Returns
921        -------
922        `aiobungie.crates.Milestone`
923            A runtime status of the clan's milestone data.
924        """
925
926        resp = await self.rest.fetch_clan_weekly_rewards(clan_id)
927
928        return self.factory.deserialize_milestone(resp)

Fetch a Bungie clan's weekly reward state.

Parameters
  • clan_id (int): The clan's id.
Returns
async def fetch_inventory_item(self, hash: int, /) -> aiobungie.crates.entity.InventoryEntity:
932    async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity:
933        """Fetch a static inventory item entity given a its hash.
934
935        Parameters
936        ----------
937        hash: `int`
938            Inventory item's hash.
939
940        Returns
941        -------
942        `aiobungie.crates.InventoryEntity`
943            A bungie inventory item.
944        """
945        resp = await self.rest.fetch_inventory_item(hash)
946
947        return self.factory.deserialize_inventory_entity(resp)

Fetch a static inventory item entity given a its hash.

Parameters
  • hash (int): Inventory item's hash.
Returns
async def fetch_objective_entity(self, hash: int, /) -> aiobungie.crates.entity.ObjectiveEntity:
949    async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity:
950        """Fetch a Destiny objective entity given a its hash.
951
952        Parameters
953        ----------
954        hash: `int`
955            objective's hash.
956
957        Returns
958        -------
959        `aiobungie.crates.ObjectiveEntity`
960            An objective entity item.
961        """
962        resp = await self.rest.fetch_objective_entity(hash)
963
964        return self.factory.deserialize_objective_entity(resp)

Fetch a Destiny objective entity given a its hash.

Parameters
  • hash (int): objective's hash.
Returns
async def search_entities( self, name: str, entity_type: str, *, page: int = 0) -> aiobungie.FlatIterator[aiobungie.crates.entity.SearchableEntity]:
966    async def search_entities(
967        self, name: str, entity_type: str, *, page: int = 0
968    ) -> iterators.FlatIterator[entity.SearchableEntity]:
969        """Search for Destiny2 entities given a name and its type.
970
971        Parameters
972        ----------
973        name : `str`
974            The name of the entity, i.e., Thunderlord, One thousand voices.
975        entity_type : `str`
976            The type of the entity, AKA Definition,
977            For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items.
978
979        Other Parameters
980        ----------------
981        page : `int`
982            An optional page to return. Default to 0.
983
984        Returns
985        -------
986        `aiobungie.iterators.FlatIterator[aiobungie.crates.SearchableEntity]`
987            An iterator over the found results matching the provided name.
988        """
989        resp = await self.rest.search_entities(name, entity_type, page=page)
990
991        return self.factory.deserialize_inventory_results(resp)

Search for Destiny2 entities given a name and its type.

Parameters
  • name (str): The name of the entity, i.e., Thunderlord, One thousand voices.
  • entity_type (str): The type of the entity, AKA Definition, For an example DestinyInventoryItemDefinition for emblems, weapons, and other inventory items.
Other Parameters
  • page (int): An optional page to return. Default to 0.
Returns
async def fetch_fireteams( self, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform] = <FireteamPlatform.ANY: 0>, language: Union[aiobungie.FireteamLanguage, str] = <FireteamLanguage.ALL: >, date_range: int = 0, page: int = 0, slots_filter: int = 0) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]]:
 995    async def fetch_fireteams(
 996        self,
 997        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
 998        *,
 999        platform: typedefs.IntAnd[
1000            fireteams.FireteamPlatform
1001        ] = fireteams.FireteamPlatform.ANY,
1002        language: typing.Union[
1003            fireteams.FireteamLanguage, str
1004        ] = fireteams.FireteamLanguage.ALL,
1005        date_range: int = 0,
1006        page: int = 0,
1007        slots_filter: int = 0,
1008    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1009        """Fetch public Bungie fireteams with open slots.
1010
1011        Parameters
1012        ----------
1013        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1014            The fireteam activity type.
1015
1016        Other Parameters
1017        ----------------
1018        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1019            If this is provided. Then the results will be filtered with the given platform.
1020            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1021        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1022            A locale language to filter the used language in that fireteam.
1023            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1024        date_range : `int`
1025            An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`.
1026        page : `int`
1027            The page number. By default its `0` which returns all available activities.
1028        slots_filter : `int`
1029            Filter the returned fireteams based on available slots. Default is `0`
1030
1031        Returns
1032        -------
1033        `typing.Optional[collections.Sequence[fireteams.Fireteam]]`
1034            A sequence of `aiobungie.crates.Fireteam` or `None`.
1035        """
1036
1037        resp = await self.rest.fetch_fireteams(
1038            activity_type,
1039            platform=platform,
1040            language=language,
1041            date_range=date_range,
1042            page=page,
1043            slots_filter=slots_filter,
1044        )
1045
1046        return self.factory.deserialize_fireteams(resp)

Fetch public Bungie fireteams with open slots.

Parameters
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (int): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_avaliable_clan_fireteams( self, access_token: str, group_id: int, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], date_range: int = 0, page: int = 0, public_only: bool = False, slots_filter: int = 0) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]]:
1048    async def fetch_avaliable_clan_fireteams(
1049        self,
1050        access_token: str,
1051        group_id: int,
1052        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1053        *,
1054        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1055        language: typing.Union[fireteams.FireteamLanguage, str],
1056        date_range: int = 0,
1057        page: int = 0,
1058        public_only: bool = False,
1059        slots_filter: int = 0,
1060    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1061        """Fetch a clan's fireteams with open slots.
1062
1063        .. note::
1064            This method requires OAuth2: ReadGroups scope.
1065
1066        Parameters
1067        ----------
1068        access_token : `str`
1069            The bearer access token associated with the bungie account.
1070        group_id : `int`
1071            The group/clan id of the fireteam.
1072        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1073            The fireteam activity type.
1074
1075        Other Parameters
1076        ----------------
1077        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1078            If this is provided. Then the results will be filtered with the given platform.
1079            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1080        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1081            A locale language to filter the used language in that fireteam.
1082            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1083        date_range : `int`
1084            An integer to filter the date range of the returned fireteams. Defaults to `0`.
1085        page : `int`
1086            The page number. By default its `0` which returns all available activities.
1087        public_only: `bool`
1088            If set to True, Then only public fireteams will be returned.
1089        slots_filter : `int`
1090            Filter the returned fireteams based on available slots. Default is `0`
1091
1092        Returns
1093        -------
1094        `typing.Optional[collections.Sequence[aiobungie.crates.Fireteam]]`
1095            A sequence of  fireteams found in the clan.
1096            `None` will be returned if nothing was found.
1097        """
1098        resp = await self.rest.fetch_avaliable_clan_fireteams(
1099            access_token,
1100            group_id,
1101            activity_type,
1102            platform=platform,
1103            language=language,
1104            date_range=date_range,
1105            page=page,
1106            public_only=public_only,
1107            slots_filter=slots_filter,
1108        )
1109
1110        return self.factory.deserialize_fireteams(resp)

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id of the fireteam.
  • activity_type (aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]): The fireteam activity type.
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (int): An integer to filter the date range of the returned fireteams. Defaults to 0.
  • page (int): The page number. By default its 0 which returns all available activities.
  • public_only (bool): If set to True, Then only public fireteams will be returned.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
  • typing.Optional[collections.Sequence[aiobungie.crates.Fireteam]]: A sequence of fireteams found in the clan. None will be returned if nothing was found.
async def fetch_clan_fireteam( self, access_token: str, fireteam_id: int, group_id: int) -> aiobungie.crates.fireteams.AvailableFireteam:
1112    async def fetch_clan_fireteam(
1113        self, access_token: str, fireteam_id: int, group_id: int
1114    ) -> fireteams.AvailableFireteam:
1115        """Fetch a specific clan fireteam.
1116
1117        .. note::
1118            This method requires OAuth2: ReadGroups scope.
1119
1120        Parameters
1121        ----------
1122        access_token : `str`
1123            The bearer access token associated with the bungie account.
1124        group_id : `int`
1125            The group/clan id to fetch the fireteam from.
1126        fireteam_id : `int`
1127            The fireteam id to fetch.
1128
1129        Returns
1130        -------
1131        `typing.Optional[aiobungie.crates.AvailableFireteam]`
1132            A sequence of available fireteams objects if exists. else `None` will be returned.
1133        """
1134        resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id)
1135
1136        return self.factory.deserialize_available_fireteams(
1137            resp, no_results=True
1138        )  # type: ignore[return-value]

Fetch a specific clan fireteam.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch the fireteam from.
  • fireteam_id (int): The fireteam id to fetch.
Returns
async def fetch_my_clan_fireteams( self, access_token: str, group_id: int, *, include_closed: bool = True, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], filtered: bool = True, page: int = 0) -> collections.abc.Sequence[aiobungie.crates.fireteams.AvailableFireteam]:
1140    async def fetch_my_clan_fireteams(
1141        self,
1142        access_token: str,
1143        group_id: int,
1144        *,
1145        include_closed: bool = True,
1146        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1147        language: typing.Union[fireteams.FireteamLanguage, str],
1148        filtered: bool = True,
1149        page: int = 0,
1150    ) -> collections.Sequence[fireteams.AvailableFireteam]:
1151        """A method that's similar to `fetch_fireteams` but requires OAuth2.
1152
1153        .. note::
1154            This method requires OAuth2: ReadGroups scope.
1155
1156        Parameters
1157        ----------
1158        access_token : str
1159            The bearer access token associated with the bungie account.
1160        group_id : int
1161            The group/clan id to fetch.
1162
1163        Other Parameters
1164        ----------------
1165        include_closed : bool
1166            If provided and set to True, It will also return closed fireteams.
1167            If provided and set to False, It will only return public fireteams. Default is True.
1168        platform : aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]
1169            If this is provided. Then the results will be filtered with the given platform.
1170            Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
1171        language : typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]
1172            A locale language to filter the used language in that fireteam.
1173            Defaults to aiobungie.crates.FireteamLanguage.ALL
1174        filtered : bool
1175            If set to True, it will filter by clan. Otherwise not. Default is True.
1176        page : int
1177            The page number. By default its 0 which returns all available activities.
1178
1179        Returns
1180        -------
1181        `collections.Sequence[aiobungie.crates.AvailableFireteam]`
1182            A sequence of available fireteams objects if exists. else `None` will be returned.
1183        """
1184        resp = await self.rest.fetch_my_clan_fireteams(
1185            access_token,
1186            group_id,
1187            include_closed=include_closed,
1188            platform=platform,
1189            language=language,
1190            filtered=filtered,
1191            page=page,
1192        )
1193
1194        return self.factory.deserialize_available_fireteams(resp)  # type: ignore[return-value]

A method that's similar to fetch_fireteams but requires OAuth2.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch.
Other Parameters
  • include_closed (bool): If provided and set to True, It will also return closed fireteams. If provided and set to False, It will only return public fireteams. Default is True.
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • filtered (bool): If set to True, it will filter by clan. Otherwise not. Default is True.
  • page (int): The page number. By default its 0 which returns all available activities.
Returns
async def fetch_friends( self, access_token: str, /) -> collections.abc.Sequence[aiobungie.crates.friends.Friend]:
1198    async def fetch_friends(
1199        self, access_token: str, /
1200    ) -> collections.Sequence[friends.Friend]:
1201        """Fetch bungie friend list.
1202
1203        .. note::
1204            This requests OAuth2: ReadUserData scope.
1205
1206        Parameters
1207        -----------
1208        access_token : `str`
1209            The bearer access token associated with the bungie account.
1210
1211        Returns
1212        -------
1213        `collections.Sequence[aiobungie.crates.Friend]`
1214            A sequence of the friends associated with that access token.
1215        """
1216
1217        resp = await self.rest.fetch_friends(access_token)
1218
1219        return self.factory.deserialize_friends(resp)

Fetch bungie friend list.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_friend_requests(self, access_token: str, /) -> aiobungie.crates.friends.FriendRequestView:
1221    async def fetch_friend_requests(
1222        self, access_token: str, /
1223    ) -> friends.FriendRequestView:
1224        """Fetch pending bungie friend requests queue.
1225
1226        .. note::
1227            This requests OAuth2: ReadUserData scope.
1228
1229        Parameters
1230        -----------
1231        access_token : `str`
1232            The bearer access token associated with the bungie account.
1233
1234        Returns
1235        -------
1236        `aiobungie.crates.FriendRequestView`
1237            A friend requests view of that associated access token.
1238        """
1239
1240        resp = await self.rest.fetch_friend_requests(access_token)
1241
1242        return self.factory.deserialize_friend_requests(resp)

Fetch pending bungie friend requests queue.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_application(self, appid: int, /) -> aiobungie.crates.application.Application:
1246    async def fetch_application(self, appid: int, /) -> application.Application:
1247        """Fetch a Bungie application.
1248
1249        Parameters
1250        -----------
1251        appid: `int`
1252            The application id.
1253
1254        Returns
1255        --------
1256        `aiobungie.crates.Application`
1257            A Bungie application.
1258        """
1259        resp = await self.rest.fetch_application(appid)
1260
1261        return self.factory.deserialize_app(resp)

Fetch a Bungie application.

Parameters
  • appid (int): The application id.
Returns
async def fetch_public_milestone_content( self, milestone_hash: int, /) -> aiobungie.crates.milestones.MilestoneContent:
1265    async def fetch_public_milestone_content(
1266        self, milestone_hash: int, /
1267    ) -> milestones.MilestoneContent:
1268        """Fetch the milestone content given its hash.
1269
1270        Parameters
1271        ----------
1272        milestone_hash : `int`
1273            The milestone hash.
1274
1275        Returns
1276        -------
1277        `aiobungie.crates.milestones.MilestoneContent`
1278            A milestone content object.
1279        """
1280        resp = await self.rest.fetch_public_milestone_content(milestone_hash)
1281
1282        return self.factory.deserialize_public_milestone_content(resp)

Fetch the milestone content given its hash.

Parameters
  • milestone_hash (int): The milestone hash.
Returns
  • aiobungie.crates.milestones.MilestoneContent: A milestone content object.
@typing.final
class ClosedReasons(aiobungie.Flag):
782@typing.final
783class ClosedReasons(Flag):
784    """A Flags enumeration representing the reasons why a person can't join this user's fireteam."""
785
786    NONE = 0
787    MATCHMAKING = 1
788    LOADING = 2
789    SOLO = 4
790    """The activity is required to be played solo."""
791    INTERNAL_REASONS = 8
792    """
793    The user can't be joined for one of a variety of internal reasons.
794    Basically, the game can't let you join at this time,
795    but for reasons that aren't under the control of this user
796    """
797    DISALLOWED_BY_GAME_STATE = 16
798    """The user's current activity/quest/other transitory game state is preventing joining."""
799    OFFLINE = 32768
800    """The user appears offline."""

A Flags enumeration representing the reasons why a person can't join this user's fireteam.

NONE = <ClosedReasons.NONE: 0>
MATCHMAKING = <ClosedReasons.MATCHMAKING: 1>
LOADING = <ClosedReasons.LOADING: 2>
SOLO = <ClosedReasons.SOLO: 4>

The activity is required to be played solo.

INTERNAL_REASONS = <ClosedReasons.INTERNAL_REASONS: 8>

The user can't be joined for one of a variety of internal reasons. Basically, the game can't let you join at this time, but for reasons that aren't under the control of this user

DISALLOWED_BY_GAME_STATE = <ClosedReasons.DISALLOWED_BY_GAME_STATE: 16>

The user's current activity/quest/other transitory game state is preventing joining.

OFFLINE = <ClosedReasons.OFFLINE: 32768>

The user appears offline.

Inherited Members
Flag
name
value
@typing.final
class ComponentFields(aiobungie.Enum):
74@typing.final
75class ComponentFields(enums.Enum):
76    """An enum that provides fields found in a base component response."""
77
78    PRIVACY = ComponentPrivacy
79    DISABLED = False

An enum that provides fields found in a base component response.

PRIVACY = <ComponentFields.PRIVACY: <enum 'ComponentPrivacy'>>
DISABLED = <ComponentFields.DISABLED: False>
Inherited Members
Enum
name
value
@typing.final
class ComponentPrivacy(builtins.int, aiobungie.Enum):
65@typing.final
66class ComponentPrivacy(int, enums.Enum):
67    """An enum the provides privacy settings for profile components."""
68
69    NONE = 0
70    PUBLIC = 1
71    PRIVATE = 2

An enum the provides privacy settings for profile components.

NONE = <ComponentPrivacy.NONE: 0>
PUBLIC = <ComponentPrivacy.PUBLIC: 1>
PRIVATE = <ComponentPrivacy.PRIVATE: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ComponentType(aiobungie.Enum):
363@typing.final
364class ComponentType(Enum):
365    """An Enum for Destiny 2 profile Components."""
366
367    NONE = 0
368
369    PROFILE = 100
370    PROFILE_INVENTORIES = 102
371    PROFILE_CURRENCIES = 103
372    PROFILE_PROGRESSION = 104
373    ALL_PROFILES = (
374        PROFILE,
375        PROFILE_INVENTORIES,
376        PROFILE_CURRENCIES,
377        PROFILE_PROGRESSION,
378    )
379    """All profile components."""
380
381    VENDORS = 400
382    VENDOR_SALES = 402
383    VENDOR_RECEIPTS = 101
384    ALL_VENDORS = (VENDORS, VENDOR_RECEIPTS, VENDOR_SALES)
385    """All vendor components."""
386
387    # Items
388    ITEM_INSTANCES = 300
389    ITEM_OBJECTIVES = 301
390    ITEM_PERKS = 302
391    ITEM_RENDER_DATA = 303
392    ITEM_STATS = 304
393    ITEM_SOCKETS = 305
394    ITEM_TALENT_GRINDS = 306
395    ITEM_PLUG_STATES = 308
396    ITEM_PLUG_OBJECTIVES = 309
397    ITEM_REUSABLE_PLUGS = 310
398
399    ALL_ITEMS = (
400        ITEM_PLUG_OBJECTIVES,
401        ITEM_PLUG_STATES,
402        ITEM_SOCKETS,
403        ITEM_INSTANCES,
404        ITEM_OBJECTIVES,
405        ITEM_PERKS,
406        ITEM_RENDER_DATA,
407        ITEM_STATS,
408        ITEM_TALENT_GRINDS,
409        ITEM_REUSABLE_PLUGS,
410    )
411    """All item components."""
412
413    PLATFORM_SILVER = 105
414    KIOSKS = 500
415    CURRENCY_LOOKUPS = 600
416    PRESENTATION_NODES = 700
417    COLLECTIBLES = 800
418    RECORDS = 900
419    TRANSITORY = 1000
420    METRICS = 1100
421    INVENTORIES = 102
422    STRING_VARIABLES = 1200
423    CRAFTABLES = 1300
424
425    CHARACTERS = 200
426    CHARACTER_INVENTORY = 201
427    CHARECTER_PROGRESSION = 202
428    CHARACTER_RENDER_DATA = 203
429    CHARACTER_ACTIVITIES = 204
430    CHARACTER_EQUIPMENT = 205
431
432    ALL_CHARACTERS = (
433        CHARACTERS,
434        CHARACTER_INVENTORY,
435        CHARECTER_PROGRESSION,
436        CHARACTER_RENDER_DATA,
437        CHARACTER_ACTIVITIES,
438        CHARACTER_EQUIPMENT,
439        RECORDS,
440    )
441    """All character components."""
442
443    ALL = (
444        *ALL_PROFILES,  # type: ignore
445        *ALL_CHARACTERS,  # type: ignore
446        *ALL_VENDORS,  # type: ignore
447        *ALL_ITEMS,  # type: ignore
448        RECORDS,
449        CURRENCY_LOOKUPS,
450        PRESENTATION_NODES,
451        COLLECTIBLES,
452        KIOSKS,
453        METRICS,
454        PLATFORM_SILVER,
455        INVENTORIES,
456        STRING_VARIABLES,
457        TRANSITORY,
458        CRAFTABLES,
459    )
460    """ALl components included."""

An Enum for Destiny 2 profile Components.

NONE = <ComponentType.NONE: 0>
PROFILE = <ComponentType.PROFILE: 100>
PROFILE_INVENTORIES = <ComponentType.PROFILE_INVENTORIES: 102>
PROFILE_CURRENCIES = <ComponentType.PROFILE_CURRENCIES: 103>
PROFILE_PROGRESSION = <ComponentType.PROFILE_PROGRESSION: 104>
ALL_PROFILES = <ComponentType.ALL_PROFILES: (100, 102, 103, 104)>

All profile components.

VENDORS = <ComponentType.VENDORS: 400>
VENDOR_SALES = <ComponentType.VENDOR_SALES: 402>
VENDOR_RECEIPTS = <ComponentType.VENDOR_RECEIPTS: 101>
ALL_VENDORS = <ComponentType.ALL_VENDORS: (400, 101, 402)>

All vendor components.

ITEM_INSTANCES = <ComponentType.ITEM_INSTANCES: 300>
ITEM_OBJECTIVES = <ComponentType.ITEM_OBJECTIVES: 301>
ITEM_PERKS = <ComponentType.ITEM_PERKS: 302>
ITEM_RENDER_DATA = <ComponentType.ITEM_RENDER_DATA: 303>
ITEM_STATS = <ComponentType.ITEM_STATS: 304>
ITEM_SOCKETS = <ComponentType.ITEM_SOCKETS: 305>
ITEM_TALENT_GRINDS = <ComponentType.ITEM_TALENT_GRINDS: 306>
ITEM_PLUG_STATES = <ComponentType.ITEM_PLUG_STATES: 308>
ITEM_PLUG_OBJECTIVES = <ComponentType.ITEM_PLUG_OBJECTIVES: 309>
ITEM_REUSABLE_PLUGS = <ComponentType.ITEM_REUSABLE_PLUGS: 310>
ALL_ITEMS = <ComponentType.ALL_ITEMS: (309, 308, 305, 300, 301, 302, 303, 304, 306, 310)>

All item components.

PLATFORM_SILVER = <ComponentType.PLATFORM_SILVER: 105>
KIOSKS = <ComponentType.KIOSKS: 500>
CURRENCY_LOOKUPS = <ComponentType.CURRENCY_LOOKUPS: 600>
PRESENTATION_NODES = <ComponentType.PRESENTATION_NODES: 700>
COLLECTIBLES = <ComponentType.COLLECTIBLES: 800>
RECORDS = <ComponentType.RECORDS: 900>
TRANSITORY = <ComponentType.TRANSITORY: 1000>
METRICS = <ComponentType.METRICS: 1100>
INVENTORIES = <ComponentType.PROFILE_INVENTORIES: 102>
STRING_VARIABLES = <ComponentType.STRING_VARIABLES: 1200>
CRAFTABLES = <ComponentType.CRAFTABLES: 1300>
CHARACTERS = <ComponentType.CHARACTERS: 200>
CHARACTER_INVENTORY = <ComponentType.CHARACTER_INVENTORY: 201>
CHARECTER_PROGRESSION = <ComponentType.CHARECTER_PROGRESSION: 202>
CHARACTER_RENDER_DATA = <ComponentType.CHARACTER_RENDER_DATA: 203>
CHARACTER_ACTIVITIES = <ComponentType.CHARACTER_ACTIVITIES: 204>
CHARACTER_EQUIPMENT = <ComponentType.CHARACTER_EQUIPMENT: 205>
ALL_CHARACTERS = <ComponentType.ALL_CHARACTERS: (200, 201, 202, 203, 204, 205, 900)>

All character components.

ALL = <ComponentType.ALL: (100, 102, 103, 104, 200, 201, 202, 203, 204, 205, 900, 400, 101, 402, 309, 308, 305, 300, 301, 302, 303, 304, 306, 310, 900, 600, 700, 800, 500, 1100, 105, 102, 1200, 1000, 1300)>

ALl components included.

Inherited Members
Enum
name
value
@typing.final
class CredentialType(builtins.int, aiobungie.Enum):
664@typing.final
665class CredentialType(int, Enum):
666    """The types of the accounts system supports at bungie."""
667
668    NONE = 0
669    XUID = 1
670    PSNID = 2
671    WILD = 3
672    FAKE = 4
673    FACEBOOK = 5
674    GOOGLE = 8
675    WINDOWS = 9
676    DEMONID = 10
677    STEAMID = 12
678    BATTLENETID = 14
679    STADIAID = 16
680    TWITCHID = 18

The types of the accounts system supports at bungie.

NONE = <CredentialType.NONE: 0>
XUID = <CredentialType.XUID: 1>
PSNID = <CredentialType.PSNID: 2>
WILD = <CredentialType.WILD: 3>
FAKE = <CredentialType.FAKE: 4>
FACEBOOK = <CredentialType.FACEBOOK: 5>
GOOGLE = <CredentialType.GOOGLE: 8>
WINDOWS = <CredentialType.WINDOWS: 9>
DEMONID = <CredentialType.DEMONID: 10>
STEAMID = <CredentialType.STEAMID: 12>
BATTLENETID = <CredentialType.BATTLENETID: 14>
STADIAID = <CredentialType.STADIAID: 16>
TWITCHID = <CredentialType.TWITCHID: 18>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class DamageType(builtins.int, aiobungie.Enum):
542@typing.final
543class DamageType(int, Enum):
544    """Enums for Destiny Damage types"""
545
546    NONE = 0
547    KINETIC = 1
548    ARC = 2
549    SOLAR = 3
550    VOID = 4
551    RAID = 5
552    """This is a special damage type reserved for some raid activity encounters."""
553    STASIS = 6

Enums for Destiny Damage types

NONE = <DamageType.NONE: 0>
KINETIC = <DamageType.KINETIC: 1>
ARC = <DamageType.ARC: 2>
SOLAR = <DamageType.SOLAR: 3>
VOID = <DamageType.VOID: 4>
RAID = <DamageType.RAID: 5>

This is a special damage type reserved for some raid activity encounters.

STASIS = <DamageType.STASIS: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Difficulty(builtins.int, aiobungie.Enum):
65@typing.final
66class Difficulty(int, enums.Enum):
67    """An enum for activities difficulties."""
68
69    TRIVIAL = 0
70    EASY = 1
71    NORMAL = 2
72    CHALLENGING = 3
73    HARD = 4
74    BRAVE = 5
75    ALMOST_IMPOSSIBLE = 6
76    IMPOSSIBLE = 7

An enum for activities difficulties.

TRIVIAL = <Difficulty.TRIVIAL: 0>
EASY = <Difficulty.EASY: 1>
NORMAL = <Difficulty.NORMAL: 2>
CHALLENGING = <Difficulty.CHALLENGING: 3>
HARD = <Difficulty.HARD: 4>
BRAVE = <Difficulty.BRAVE: 5>
ALMOST_IMPOSSIBLE = <Difficulty.ALMOST_IMPOSSIBLE: 6>
IMPOSSIBLE = <Difficulty.IMPOSSIBLE: 7>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Dungeon(builtins.int, aiobungie.Enum):
165@typing.final
166class Dungeon(int, Enum):
167    """An Enum for all available Dungeon/Like missions in Destiny 2."""
168
169    NORMAL_PRESAGE = 2124066889
170    """Normal Presage"""
171
172    MASTER_PRESAGE = 4212753278
173    """Master Presage"""
174
175    HARBINGER = 1738383283
176    """Harbinger"""
177
178    PROPHECY = 4148187374
179    """Prophecy"""
180
181    MASTER_POH = 785700673
182    """Master Pit of Heresy?"""
183
184    LEGEND_POH = 785700678
185    """Legend Pit of Heresy?"""
186
187    POH = 1375089621
188    """Normal Pit of Heresy."""
189
190    SHATTERED = 2032534090
191    """Shattered Throne"""
192
193    GOA_LEGEND = 4078656646
194    """Grasp of Avarice legend."""
195
196    GOA_MASTER = 3774021532
197    """Grasp of Avarice master."""

An Enum for all available Dungeon/Like missions in Destiny 2.

NORMAL_PRESAGE = <Dungeon.NORMAL_PRESAGE: 2124066889>

Normal Presage

MASTER_PRESAGE = <Dungeon.MASTER_PRESAGE: 4212753278>

Master Presage

HARBINGER = <Dungeon.HARBINGER: 1738383283>

Harbinger

PROPHECY = <Dungeon.PROPHECY: 4148187374>

Prophecy

MASTER_POH = <Dungeon.MASTER_POH: 785700673>

Master Pit of Heresy?

LEGEND_POH = <Dungeon.LEGEND_POH: 785700678>

Legend Pit of Heresy?

POH = <Dungeon.POH: 1375089621>

Normal Pit of Heresy.

SHATTERED = <Dungeon.SHATTERED: 2032534090>

Shattered Throne

GOA_LEGEND = <Dungeon.GOA_LEGEND: 4078656646>

Grasp of Avarice legend.

GOA_MASTER = <Dungeon.GOA_MASTER: 3774021532>

Grasp of Avarice master.

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Enum(enum.Enum):
77class Enum(__enum.Enum):
78    """Builtin Python enum with extra handlings."""
79
80    @property
81    def name(self) -> str:  # type: ignore[override]
82        return self._name_
83
84    @property
85    def value(self) -> typing.Any:  # type: ignore[override]
86        return self._value_
87
88    def __str__(self) -> str:
89        return self._name_
90
91    def __repr__(self) -> str:
92        return f"<{type(self).__name__}.{self._name_}: {self._value_!s}>"
93
94    def __int__(self) -> int:
95        if isinstance(self.value, _ITERABLE):
96            raise TypeError(
97                f"Can't overload {self.value} in {type(self).__name__}, Please use `.value` attribute.",
98            )
99        return int(self.value)

Builtin Python enum with extra handlings.

name: str
value: Any
class Factory(aiobungie.interfaces.factory.FactoryInterface):
  61class Factory(interfaces.FactoryInterface):
  62    """The base deserialization factory class for all aiobungie objects.
  63
  64    Highly inspired hikari entity factory used to deserialize JSON responses from the REST client and turning them
  65    into a `aiobungie.crates` Python classes.
  66    """
  67
  68    __slots__ = ("_net",)
  69
  70    def __init__(self, net: traits.Netrunner) -> None:
  71        self._net = net
  72
  73    def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser:
  74        return user.BungieUser(
  75            id=int(data["membershipId"]),
  76            created_at=time.clean_date(data["firstAccess"]),
  77            name=data.get("cachedBungieGlobalDisplayName", undefined.Undefined),
  78            is_deleted=data["isDeleted"],
  79            about=data["about"],
  80            updated_at=time.clean_date(data["lastUpdate"]),
  81            psn_name=data.get("psnDisplayName", None),
  82            stadia_name=data.get("stadiaDisplayName", None),
  83            steam_name=data.get("steamDisplayName", None),
  84            twitch_name=data.get("twitchDisplayName", None),
  85            blizzard_name=data.get("blizzardDisplayName", None),
  86            status=data["statusText"],
  87            locale=data["locale"],
  88            picture=assets.Image(path=str(data["profilePicturePath"])),
  89            code=data.get("cachedBungieGlobalDisplayNameCode", None),
  90            unique_name=data.get("uniqueName", None),
  91            theme_id=int(data["profileTheme"]),
  92            show_activity=bool(data["showActivity"]),
  93            theme_name=data["profileThemeName"],
  94            display_title=data["userTitleDisplay"],
  95        )
  96
  97    def deserialize_partial_bungie_user(
  98        self, payload: typedefs.JSONObject
  99    ) -> user.PartialBungieUser:
 100        return user.PartialBungieUser(
 101            net=self._net,
 102            types=[
 103                enums.MembershipType(type_)
 104                for type_ in payload.get("applicableMembershipTypes", [])
 105            ],
 106            name=payload.get("displayName", undefined.Undefined),
 107            id=int(payload["membershipId"]),
 108            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
 109            is_public=payload["isPublic"],
 110            icon=assets.Image(payload.get("iconPath", "")),
 111            type=enums.MembershipType(payload["membershipType"]),
 112        )
 113
 114    def deserialize_destiny_membership(
 115        self, payload: typedefs.JSONObject
 116    ) -> user.DestinyMembership:
 117        name: undefined.UndefinedOr[str] = undefined.Undefined
 118        if (
 119            raw_name := payload.get("bungieGlobalDisplayName", "")
 120        ) and not typedefs.is_unknown(raw_name):
 121            name = raw_name
 122
 123        return user.DestinyMembership(
 124            net=self._net,
 125            id=int(payload["membershipId"]),
 126            name=name,
 127            code=payload.get("bungieGlobalDisplayNameCode", None),
 128            last_seen_name=payload.get("LastSeenDisplayName")
 129            or payload.get("displayName")  # noqa: W503
 130            or "",  # noqa: W503
 131            type=enums.MembershipType(payload["membershipType"]),
 132            is_public=payload["isPublic"],
 133            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
 134            icon=assets.Image(payload.get("iconPath", "")),
 135            types=[
 136                enums.MembershipType(type_)
 137                for type_ in payload.get("applicableMembershipTypes", [])
 138            ],
 139        )
 140
 141    def deserialize_destiny_memberships(
 142        self, data: typedefs.JSONArray
 143    ) -> collections.Sequence[user.DestinyMembership]:
 144        return [self.deserialize_destiny_membership(membership) for membership in data]
 145
 146    def deserialize_user(self, data: typedefs.JSONObject) -> user.User:
 147
 148        primary_membership_id: typing.Optional[int] = None
 149        if raw_primary_id := data.get("primaryMembershipId"):
 150            primary_membership_id = int(raw_primary_id)
 151
 152        return user.User(
 153            bungie=self.deserialize_bungie_user(data["bungieNetUser"]),
 154            destiny=self.deserialize_destiny_memberships(data["destinyMemberships"]),
 155            primary_membership_id=primary_membership_id,
 156        )
 157
 158    def deserialize_searched_user(
 159        self, payload: typedefs.JSONObject
 160    ) -> user.SearchableDestinyUser:
 161        name: undefined.UndefinedOr[str] = undefined.Undefined
 162        if (raw_name := payload["bungieGlobalDisplayName"]) and not typedefs.is_unknown(
 163            raw_name
 164        ):
 165            name = raw_name
 166
 167        code: typing.Optional[int] = None
 168        if raw_code := payload.get("bungieGlobalDisplayNameCode"):
 169            code = int(raw_code)
 170
 171        bungie_id: typing.Optional[int] = None
 172        if raw_bungie_id := payload.get("bungieNetMembershipId"):
 173            bungie_id = int(raw_bungie_id)
 174
 175        return user.SearchableDestinyUser(
 176            name=name,
 177            code=code,
 178            bungie_id=bungie_id,
 179            memberships=self.deserialize_destiny_memberships(
 180                payload["destinyMemberships"]
 181            ),
 182        )
 183
 184    def deserialize_user_credentials(
 185        self, payload: typedefs.JSONArray
 186    ) -> collections.Sequence[user.UserCredentials]:
 187        return [
 188            user.UserCredentials(
 189                type=enums.CredentialType(int(creds["credentialType"])),
 190                display_name=creds["credentialDisplayName"],
 191                is_public=creds["isPublic"],
 192                self_as_string=creds.get("credentialAsString", undefined.Undefined),
 193            )
 194            for creds in payload
 195        ]
 196
 197    @staticmethod
 198    def set_themese_attrs(
 199        payload: typedefs.JSONArray, /
 200    ) -> typing.Collection[user.UserThemes]:
 201        return [
 202            user.UserThemes(
 203                id=int(entry["userThemeId"]),
 204                name=entry["userThemeName"]
 205                if "userThemeName" in entry
 206                else undefined.Undefined,
 207                description=entry["userThemeDescription"]
 208                if "userThemeDescription" in entry
 209                else undefined.Undefined,
 210            )
 211            for entry in payload
 212        ]
 213
 214    def deserialize_user_themes(
 215        self, payload: typedefs.JSONArray
 216    ) -> collections.Sequence[user.UserThemes]:
 217        return list(self.set_themese_attrs(payload))
 218
 219    def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan:
 220
 221        # This is kinda redundant
 222        data = payload
 223
 224        # This is always outside the details.
 225        current_user_map: typing.Optional[
 226            collections.Mapping[str, clans.ClanMember]
 227        ] = None
 228        if raw_current_user_map := payload.get("currentUserMemberMap"):
 229            current_user_map = {
 230                membership_type: self.deserialize_clan_member(membership)
 231                for membership_type, membership in raw_current_user_map.items()
 232            }
 233
 234        try:
 235            data = payload["detail"]
 236        except KeyError:
 237            pass
 238
 239        id = data["groupId"]
 240        name = data["name"]
 241        created_at = data["creationDate"]
 242        member_count = data["memberCount"]
 243        about = data["about"]
 244        motto = data["motto"]
 245        is_public = data["isPublic"]
 246        banner = assets.Image(str(data["bannerPath"]))
 247        avatar = assets.Image(str(data["avatarPath"]))
 248        tags = data["tags"]
 249        type = data["groupType"]
 250
 251        features = data["features"]
 252        features_obj = clans.ClanFeatures(
 253            max_members=features["maximumMembers"],
 254            max_membership_types=features["maximumMembershipsOfGroupType"],
 255            capabilities=features["capabilities"],
 256            membership_types=features["membershipTypes"],
 257            invite_permissions=features["invitePermissionOverride"],
 258            update_banner_permissions=features["updateBannerPermissionOverride"],
 259            update_culture_permissions=features["updateCulturePermissionOverride"],
 260            join_level=features["joinLevel"],
 261        )
 262
 263        information: typedefs.JSONObject = data["clanInfo"]
 264        progression: collections.Mapping[int, progressions.Progression] = {
 265            int(prog_hash): self.deserialize_progressions(prog)
 266            for prog_hash, prog in information["d2ClanProgressions"].items()
 267        }
 268
 269        founder: typedefs.NoneOr[clans.ClanMember] = None
 270        if raw_founder := payload.get("founder"):
 271            founder = self.deserialize_clan_member(raw_founder)
 272
 273        return clans.Clan(
 274            net=self._net,
 275            id=int(id),
 276            name=name,
 277            type=enums.GroupType(type),
 278            created_at=time.clean_date(created_at),
 279            member_count=member_count,
 280            motto=motto,
 281            about=about,
 282            is_public=is_public,
 283            banner=banner,
 284            avatar=avatar,
 285            tags=tags,
 286            features=features_obj,
 287            owner=founder,
 288            progressions=progression,
 289            call_sign=information["clanCallsign"],
 290            banner_data=information["clanBannerData"],
 291            chat_security=data["chatSecurity"],
 292            conversation_id=int(data["conversationId"]),
 293            allow_chat=data["allowChat"],
 294            theme=data["theme"],
 295            current_user_membership=current_user_map,
 296        )
 297
 298    def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember:
 299        destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"])
 300        return clans.ClanMember(
 301            net=self._net,
 302            last_seen_name=destiny_user.last_seen_name,
 303            id=destiny_user.id,
 304            name=destiny_user.name,
 305            icon=destiny_user.icon,
 306            last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])),
 307            group_id=int(data["groupId"]),
 308            joined_at=time.clean_date(data["joinDate"]),
 309            types=destiny_user.types,
 310            is_public=destiny_user.is_public,
 311            type=destiny_user.type,
 312            code=destiny_user.code,
 313            is_online=data["isOnline"],
 314            crossave_override=destiny_user.crossave_override,
 315            bungie=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"])
 316            if "bungieNetUserInfo" in data
 317            else None,
 318            member_type=enums.ClanMemberType(int(data["memberType"])),
 319        )
 320
 321    def deserialize_clan_members(
 322        self, data: typedefs.JSONObject, /
 323    ) -> iterators.FlatIterator[clans.ClanMember]:
 324        return iterators.FlatIterator(
 325            [self.deserialize_clan_member(member) for member in data["results"]]
 326        )
 327
 328    def deserialize_group_member(
 329        self, payload: typedefs.JSONObject
 330    ) -> clans.GroupMember:
 331        member = payload["member"]
 332        return clans.GroupMember(
 333            net=self._net,
 334            join_date=time.clean_date(member["joinDate"]),
 335            group_id=int(member["groupId"]),
 336            member_type=enums.ClanMemberType(member["memberType"]),
 337            is_online=member["isOnline"],
 338            last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])),
 339            inactive_memberships=payload.get("areAllMembershipsInactive", None),
 340            member=self.deserialize_destiny_membership(member["destinyUserInfo"]),
 341            group=self.deserialize_clan(payload["group"]),
 342        )
 343
 344    def _deserialize_clan_conversation(
 345        self, payload: typedefs.JSONObject
 346    ) -> clans.ClanConversation:
 347        return clans.ClanConversation(
 348            net=self._net,
 349            id=int(payload["conversationId"]),
 350            group_id=int(payload["groupId"]),
 351            name=(
 352                payload["chatName"]
 353                if not typedefs.is_unknown(payload["chatName"])
 354                else undefined.Undefined
 355            ),
 356            chat_enabled=payload["chatEnabled"],
 357            security=payload["chatSecurity"],
 358        )
 359
 360    def deserialize_clan_conversations(
 361        self, payload: typedefs.JSONArray
 362    ) -> collections.Sequence[clans.ClanConversation]:
 363        return [self._deserialize_clan_conversation(conv) for conv in payload]
 364
 365    def deserialize_app_owner(
 366        self, payload: typedefs.JSONObject
 367    ) -> application.ApplicationOwner:
 368        return application.ApplicationOwner(
 369            net=self._net,
 370            name=payload.get("bungieGlobalDisplayName", undefined.Undefined),
 371            id=int(payload["membershipId"]),
 372            type=enums.MembershipType(payload["membershipType"]),
 373            icon=assets.Image(str(payload["iconPath"])),
 374            is_public=payload["isPublic"],
 375            code=payload.get("bungieGlobalDisplayNameCode", None),
 376        )
 377
 378    def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application:
 379        return application.Application(
 380            id=int(payload["applicationId"]),
 381            name=payload["name"],
 382            link=payload["link"],
 383            status=payload["status"],
 384            redirect_url=payload.get("redirectUrl", None),
 385            created_at=time.clean_date(str(payload["creationDate"])),
 386            published_at=time.clean_date(str(payload["firstPublished"])),
 387            owner=self.deserialize_app_owner(payload["team"][0]["user"]),  # type: ignore
 388            scope=payload.get("scope", undefined.Undefined),
 389        )
 390
 391    def _set_character_attrs(self, payload: typedefs.JSONObject) -> character.Character:
 392        total_time = time.format_played(int(payload["minutesPlayedTotal"]), suffix=True)
 393        return character.Character(
 394            net=self._net,
 395            id=int(payload["characterId"]),
 396            gender=enums.Gender(payload["genderType"]),
 397            race=enums.Race(payload["raceType"]),
 398            class_type=enums.Class(payload["classType"]),
 399            emblem=assets.Image(str(payload["emblemBackgroundPath"])),
 400            emblem_icon=assets.Image(str(payload["emblemPath"])),
 401            emblem_hash=int(payload["emblemHash"]),
 402            last_played=time.clean_date(payload["dateLastPlayed"]),
 403            total_played_time=total_time,
 404            member_id=int(payload["membershipId"]),
 405            member_type=enums.MembershipType(payload["membershipType"]),
 406            level=payload["baseCharacterLevel"],
 407            title_hash=payload.get("titleRecordHash", None),
 408            light=payload["light"],
 409            stats={enums.Stat(int(k)): v for k, v in payload["stats"].items()},
 410        )
 411
 412    def deserialize_profile(
 413        self, payload: typedefs.JSONObject, /
 414    ) -> typing.Optional[profile.Profile]:
 415        if (raw_profile := payload.get("data")) is None:
 416            return None
 417
 418        payload = raw_profile
 419        id = int(payload["userInfo"]["membershipId"])
 420        name = payload["userInfo"]["displayName"]
 421        is_public = payload["userInfo"]["isPublic"]
 422        type = enums.MembershipType(payload["userInfo"]["membershipType"])
 423        last_played = time.clean_date(str(payload["dateLastPlayed"]))
 424        character_ids = [int(cid) for cid in payload["characterIds"]]
 425        power_cap = payload["currentSeasonRewardPowerCap"]
 426
 427        return profile.Profile(
 428            id=int(id),
 429            name=name,
 430            is_public=is_public,
 431            type=type,
 432            last_played=last_played,
 433            character_ids=character_ids,
 434            power_cap=power_cap,
 435            net=self._net,
 436        )
 437
 438    def deserialize_profile_item(
 439        self, payload: typedefs.JSONObject
 440    ) -> profile.ProfileItemImpl:
 441
 442        instance_id: typing.Optional[int] = None
 443        if raw_instance_id := payload.get("itemInstanceId"):
 444            instance_id = int(raw_instance_id)
 445
 446        version_number: typing.Optional[int] = None
 447        if raw_version := payload.get("versionNumber"):
 448            version_number = int(raw_version)
 449
 450        transfer_status = enums.TransferStatus(payload["transferStatus"])
 451
 452        return profile.ProfileItemImpl(
 453            net=self._net,
 454            hash=payload["itemHash"],
 455            quantity=payload["quantity"],
 456            bind_status=enums.ItemBindStatus(payload["bindStatus"]),
 457            location=enums.ItemLocation(payload["location"]),
 458            bucket=payload["bucketHash"],
 459            transfer_status=transfer_status,
 460            lockable=payload["lockable"],
 461            state=enums.ItemState(payload["state"]),
 462            dismantel_permissions=payload["dismantlePermission"],
 463            is_wrapper=payload["isWrapper"],
 464            instance_id=instance_id,
 465            version_number=version_number,
 466            ornament_id=payload.get("overrideStyleItemHash"),
 467        )
 468
 469    def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective:
 470        return records.Objective(
 471            net=self._net,
 472            hash=payload["objectiveHash"],
 473            visible=payload["visible"],
 474            complete=payload["complete"],
 475            completion_value=payload["completionValue"],
 476            progress=payload.get("progress"),
 477            destination_hash=payload.get("destinationHash"),
 478            activity_hash=payload.get("activityHash"),
 479        )
 480
 481    def deserialize_records(
 482        self,
 483        payload: typedefs.JSONObject,
 484        scores: typing.Optional[records.RecordScores] = None,
 485        **nodes: int,
 486    ) -> records.Record:
 487        objectives: typing.Optional[list[records.Objective]] = None
 488        interval_objectives: typing.Optional[list[records.Objective]] = None
 489        record_state: typedefs.IntAnd[records.RecordState]
 490
 491        record_state = records.RecordState(payload["state"])
 492
 493        if raw_objs := payload.get("objectives"):
 494            objectives = [self.deserialize_objectives(obj) for obj in raw_objs]
 495
 496        if raw_interval_objs := payload.get("intervalObjectives"):
 497            interval_objectives = [
 498                self.deserialize_objectives(obj) for obj in raw_interval_objs
 499            ]
 500
 501        return records.Record(
 502            scores=scores,
 503            categories_node_hash=nodes.get("categories_hash", undefined.Undefined),
 504            seals_node_hash=nodes.get("seals_hash", undefined.Undefined),
 505            state=record_state,
 506            objectives=objectives,
 507            interval_objectives=interval_objectives,
 508            redeemed_count=payload.get("intervalsRedeemedCount", 0),
 509            completion_times=payload.get("completedCount", None),
 510            reward_visibility=payload.get("rewardVisibilty", None),
 511        )
 512
 513    def deserialize_character_records(
 514        self,
 515        payload: typedefs.JSONObject,
 516        scores: typing.Optional[records.RecordScores] = None,
 517        record_hashes: typing.Optional[list[int]] = None,
 518    ) -> records.CharacterRecord:
 519
 520        record = self.deserialize_records(payload, scores)
 521        return records.CharacterRecord(
 522            scores=scores,
 523            categories_node_hash=record.categories_node_hash,
 524            seals_node_hash=record.seals_node_hash,
 525            state=record.state,
 526            objectives=record.objectives,
 527            interval_objectives=record.interval_objectives,
 528            redeemed_count=payload.get("intervalsRedeemedCount", 0),
 529            completion_times=payload.get("completedCount"),
 530            reward_visibility=payload.get("rewardVisibilty"),
 531            record_hashes=record_hashes or [],
 532        )
 533
 534    def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye:
 535        return character.Dye(
 536            channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"]
 537        )
 538
 539    def deserialize_character_customization(
 540        self, payload: typedefs.JSONObject
 541    ) -> character.CustomizationOptions:
 542        return character.CustomizationOptions(
 543            personality=payload["personality"],
 544            face=payload["face"],
 545            skin_color=payload["skinColor"],
 546            lip_color=payload["lipColor"],
 547            eye_color=payload["eyeColor"],
 548            hair_colors=payload.get("hairColors", []),
 549            feature_colors=payload.get("featureColors", []),
 550            decal_color=payload["decalColor"],
 551            wear_helmet=payload["wearHelmet"],
 552            hair_index=payload["hairIndex"],
 553            feature_index=payload["featureIndex"],
 554            decal_index=payload["decalIndex"],
 555        )
 556
 557    def deserialize_character_minimal_equipments(
 558        self, payload: typedefs.JSONObject
 559    ) -> character.MinimalEquipments:
 560        dyes = None
 561        if raw_dyes := payload.get("dyes"):
 562            if raw_dyes:
 563                dyes = [self.deserialize_character_dye(dye) for dye in raw_dyes]
 564        return character.MinimalEquipments(
 565            net=self._net, item_hash=payload["itemHash"], dyes=dyes
 566        )
 567
 568    def deserialize_character_render_data(
 569        self, payload: typedefs.JSONObject, /
 570    ) -> character.RenderedData:
 571        return character.RenderedData(
 572            net=self._net,
 573            customization=self.deserialize_character_customization(
 574                payload["customization"]
 575            ),
 576            custom_dyes=[
 577                self.deserialize_character_dye(dye)
 578                for dye in payload["customDyes"]
 579                if dye
 580            ],
 581            equipment=[
 582                self.deserialize_character_minimal_equipments(equipment)
 583                for equipment in payload["peerView"]["equipment"]
 584            ],
 585        )
 586
 587    def deserialize_available_activity(
 588        self, payload: typedefs.JSONObject
 589    ) -> activity.AvailableActivity:
 590        return activity.AvailableActivity(
 591            hash=payload["activityHash"],
 592            is_new=payload["isNew"],
 593            is_completed=payload["isCompleted"],
 594            is_visible=payload["isVisible"],
 595            display_level=payload.get("displayLevel"),
 596            recommended_light=payload.get("recommendedLight"),
 597            difficulty=activity.Difficulty(payload["difficultyTier"]),
 598            can_join=payload["canJoin"],
 599            can_lead=payload["canLead"],
 600        )
 601
 602    def deserialize_character_activity(
 603        self, payload: typedefs.JSONObject
 604    ) -> activity.CharacterActivity:
 605        current_mode: typing.Optional[enums.GameMode] = None
 606        if raw_current_mode := payload.get("currentActivityModeType"):
 607            current_mode = enums.GameMode(raw_current_mode)
 608
 609        current_mode_types: typing.Optional[collections.Sequence[enums.GameMode]] = None
 610        if raw_current_modes := payload.get("currentActivityModeTypes"):
 611            current_mode_types = [enums.GameMode(type_) for type_ in raw_current_modes]
 612
 613        return activity.CharacterActivity(
 614            date_started=time.clean_date(payload["dateActivityStarted"]),
 615            current_hash=payload["currentActivityHash"],
 616            current_mode_hash=payload["currentActivityModeHash"],
 617            current_mode=current_mode,
 618            current_mode_hashes=payload.get("currentActivityModeHashes"),
 619            current_mode_types=current_mode_types,
 620            current_playlist_hash=payload.get("currentPlaylistActivityHash"),
 621            last_story_hash=payload["lastCompletedStoryHash"],
 622            available_activities=[
 623                self.deserialize_available_activity(activity_)
 624                for activity_ in payload["availableActivities"]
 625            ],
 626        )
 627
 628    def deserialize_profile_items(
 629        self, payload: typedefs.JSONObject, /
 630    ) -> list[profile.ProfileItemImpl]:
 631        return [self.deserialize_profile_item(item) for item in payload["items"]]
 632
 633    def _deserialize_node(self, payload: typedefs.JSONObject) -> records.Node:
 634        return records.Node(
 635            state=int(payload["state"]),
 636            objective=self.deserialize_objectives(payload["objective"])
 637            if "objective" in payload
 638            else None,
 639            progress_value=int(payload["progressValue"]),
 640            completion_value=int(payload["completionValue"]),
 641            record_category_score=int(payload["recordCategoryScore"])
 642            if "recordCategoryScore" in payload
 643            else None,
 644        )
 645
 646    @staticmethod
 647    def _deserialize_collectible(payload: typedefs.JSONObject) -> items.Collectible:
 648        recent_collectibles: typing.Optional[collections.Collection[int]] = None
 649        if raw_recent_collectibles := payload.get("recentCollectibleHashes"):
 650            recent_collectibles = [
 651                int(item_hash) for item_hash in raw_recent_collectibles
 652            ]
 653
 654        collectibles: dict[int, int] = {}
 655        for item_hash, mapping in payload["collectibles"].items():
 656            collectibles[int(item_hash)] = int(mapping["state"])
 657
 658        return items.Collectible(
 659            recent_collectibles=recent_collectibles,
 660            collectibles=collectibles,
 661            collection_categorie_hash=int(payload["collectionCategoriesRootNodeHash"]),
 662            collection_badges_hash=int(payload["collectionBadgesRootNodeHash"]),
 663        )
 664
 665    @staticmethod
 666    def _deserialize_currencies(
 667        payload: typedefs.JSONObject,
 668    ) -> collections.Sequence[items.Currency]:
 669        return [
 670            items.Currency(hash=int(item_hash), amount=int(amount))
 671            for item_hash, amount in payload["itemQuantities"].items()
 672        ]
 673
 674    def deserialize_progressions(
 675        self, payload: typedefs.JSONObject
 676    ) -> progressions.Progression:
 677        return progressions.Progression(
 678            hash=int(payload["progressionHash"]),
 679            level=int(payload["level"]),
 680            cap=int(payload["levelCap"]),
 681            daily_limit=int(payload["dailyLimit"]),
 682            weekly_limit=int(payload["weeklyLimit"]),
 683            current_progress=int(payload["currentProgress"]),
 684            daily_progress=int(payload["dailyProgress"]),
 685            needed=int(payload["progressToNextLevel"]),
 686            next_level=int(payload["nextLevelAt"]),
 687        )
 688
 689    def _deserialize_factions(
 690        self, payload: typedefs.JSONObject
 691    ) -> progressions.Factions:
 692        progs = self.deserialize_progressions(payload)
 693        return progressions.Factions(
 694            hash=progs.hash,
 695            level=progs.level,
 696            cap=progs.cap,
 697            daily_limit=progs.daily_limit,
 698            weekly_limit=progs.weekly_limit,
 699            current_progress=progs.current_progress,
 700            daily_progress=progs.daily_progress,
 701            needed=progs.needed,
 702            next_level=progs.next_level,
 703            faction_hash=payload["factionHash"],
 704            faction_vendor_hash=payload["factionVendorIndex"],
 705        )
 706
 707    def _deserialize_milestone_available_quest(
 708        self, payload: typedefs.JSONObject
 709    ) -> milestones.MilestoneQuest:
 710        return milestones.MilestoneQuest(
 711            item_hash=payload["questItemHash"],
 712            status=self._deserialize_milestone_quest_status(payload["status"]),
 713        )
 714
 715    def _deserialize_milestone_activity(
 716        self, payload: typedefs.JSONObject
 717    ) -> milestones.MilestoneActivity:
 718
 719        phases: typing.Optional[
 720            collections.Sequence[milestones.MilestoneActivityPhase]
 721        ] = None
 722        if raw_phases := payload.get("phases"):
 723            phases = [
 724                milestones.MilestoneActivityPhase(
 725                    is_completed=obj["complete"], hash=obj["phaseHash"]
 726                )
 727                for obj in raw_phases
 728            ]
 729
 730        return milestones.MilestoneActivity(
 731            hash=payload["activityHash"],
 732            challenges=[
 733                self.deserialize_objectives(obj["objective"])
 734                for obj in payload["challenges"]
 735            ],
 736            modifier_hashes=payload.get("modifierHashes"),
 737            boolean_options=payload.get("booleanActivityOptions"),
 738            phases=phases,
 739        )
 740
 741    def _deserialize_milestone_quest_status(
 742        self, payload: typedefs.JSONObject
 743    ) -> milestones.QuestStatus:
 744        return milestones.QuestStatus(
 745            net=self._net,
 746            quest_hash=payload["questHash"],
 747            step_hash=payload["stepHash"],
 748            step_objectives=[
 749                self.deserialize_objectives(objective)
 750                for objective in payload["stepObjectives"]
 751            ],
 752            is_tracked=payload["tracked"],
 753            is_completed=payload["completed"],
 754            started=payload["started"],
 755            item_instance_id=payload["itemInstanceId"],
 756            vendor_hash=payload.get("vendorHash"),
 757            is_redeemed=payload["redeemed"],
 758        )
 759
 760    def _deserialize_milestone_rewards(
 761        self, payload: typedefs.JSONObject
 762    ) -> milestones.MilestoneReward:
 763        return milestones.MilestoneReward(
 764            category_hash=payload["rewardCategoryHash"],
 765            entries=[
 766                milestones.MilestoneRewardEntry(
 767                    entry_hash=entry["rewardEntryHash"],
 768                    is_earned=entry["earned"],
 769                    is_redeemed=entry["redeemed"],
 770                )
 771                for entry in payload["entries"]
 772            ],
 773        )
 774
 775    def deserialize_milestone(
 776        self, payload: typedefs.JSONObject
 777    ) -> milestones.Milestone:
 778        start_date: typing.Optional[datetime.datetime] = None
 779        if raw_start_date := payload.get("startDate"):
 780            start_date = time.clean_date(raw_start_date)
 781
 782        end_date: typing.Optional[datetime.datetime] = None
 783        if raw_end_date := payload.get("endDate"):
 784            end_date = time.clean_date(raw_end_date)
 785
 786        rewards: typing.Optional[
 787            collections.Collection[milestones.MilestoneReward]
 788        ] = None
 789        if raw_rewards := payload.get("rewards"):
 790            rewards = [
 791                self._deserialize_milestone_rewards(reward) for reward in raw_rewards
 792            ]
 793
 794        activities: typing.Optional[
 795            collections.Sequence[milestones.MilestoneActivity]
 796        ] = None
 797        if raw_activities := payload.get("activities"):
 798            activities = [
 799                self._deserialize_milestone_activity(active)
 800                for active in raw_activities
 801            ]
 802
 803        quests: typing.Optional[collections.Sequence[milestones.MilestoneQuest]] = None
 804        if raw_quests := payload.get("availableQuests"):
 805            quests = [
 806                self._deserialize_milestone_available_quest(quest)
 807                for quest in raw_quests
 808            ]
 809
 810        vendors: typing.Optional[
 811            collections.Sequence[milestones.MilestoneVendor]
 812        ] = None
 813        if raw_vendors := payload.get("vendors"):
 814            vendors = [
 815                milestones.MilestoneVendor(
 816                    vendor_hash=vendor["vendorHash"],
 817                    preview_itemhash=vendor.get("previewItemHash"),
 818                )
 819                for vendor in raw_vendors
 820            ]
 821
 822        return milestones.Milestone(
 823            hash=payload["milestoneHash"],
 824            start_date=start_date,
 825            end_date=end_date,
 826            order=payload["order"],
 827            rewards=rewards,
 828            available_quests=quests,
 829            activities=activities,
 830            vendors=vendors,
 831        )
 832
 833    def _deserialize_artifact_tiers(
 834        self, payload: typedefs.JSONObject
 835    ) -> season.ArtifactTier:
 836        return season.ArtifactTier(
 837            hash=payload["tierHash"],
 838            is_unlocked=payload["isUnlocked"],
 839            points_to_unlock=payload["pointsToUnlock"],
 840            items=[
 841                season.ArtifactTierItem(
 842                    hash=item["itemHash"], is_active=item["isActive"]
 843                )
 844                for item in payload["items"]
 845            ],
 846        )
 847
 848    def deserialize_characters(
 849        self, payload: typedefs.JSONObject
 850    ) -> collections.Mapping[int, character.Character]:
 851        return {
 852            int(char_id): self._set_character_attrs(char)
 853            for char_id, char in payload["data"].items()
 854        }
 855
 856    def deserialize_character(
 857        self, payload: typedefs.JSONObject
 858    ) -> character.Character:
 859        return self._set_character_attrs(payload)
 860
 861    def deserialize_character_equipments(
 862        self, payload: typedefs.JSONObject
 863    ) -> collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]:
 864        return {
 865            int(char_id): self.deserialize_profile_items(item)
 866            for char_id, item in payload["data"].items()
 867        }
 868
 869    def deserialize_character_activities(
 870        self, payload: typedefs.JSONObject
 871    ) -> collections.Mapping[int, activity.CharacterActivity]:
 872        return {
 873            int(char_id): self.deserialize_character_activity(data)
 874            for char_id, data in payload["data"].items()
 875        }
 876
 877    def deserialize_characters_render_data(
 878        self, payload: typedefs.JSONObject
 879    ) -> collections.Mapping[int, character.RenderedData]:
 880        return {
 881            int(char_id): self.deserialize_character_render_data(data)
 882            for char_id, data in payload["data"].items()
 883        }
 884
 885    def deserialize_character_progressions(
 886        self, payload: typedefs.JSONObject
 887    ) -> character.CharacterProgression:
 888        progressions_ = {
 889            int(prog_id): self.deserialize_progressions(prog)
 890            for prog_id, prog in payload["progressions"].items()
 891        }
 892
 893        factions = {
 894            int(faction_id): self._deserialize_factions(faction)
 895            for faction_id, faction in payload["factions"].items()
 896        }
 897
 898        milestones_ = {
 899            int(milestone_hash): self.deserialize_milestone(milestone)
 900            for milestone_hash, milestone in payload["milestones"].items()
 901        }
 902
 903        uninstanced_item_objectives = {
 904            int(item_hash): [self.deserialize_objectives(ins) for ins in obj]
 905            for item_hash, obj in payload["uninstancedItemObjectives"].items()
 906        }
 907
 908        artifact = payload["seasonalArtifact"]
 909        seasonal_artifact = season.CharacterScopedArtifact(
 910            hash=artifact["artifactHash"],
 911            points_used=artifact["pointsUsed"],
 912            reset_count=artifact["resetCount"],
 913            tiers=[
 914                self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"]
 915            ],
 916        )
 917        checklists = payload["checklists"]
 918
 919        return character.CharacterProgression(
 920            progressions=progressions_,
 921            factions=factions,
 922            checklists=checklists,
 923            milestones=milestones_,
 924            seasonal_artifact=seasonal_artifact,
 925            uninstanced_item_objectives=uninstanced_item_objectives,
 926        )
 927
 928    def deserialize_character_progressions_mapping(
 929        self, payload: typedefs.JSONObject
 930    ) -> collections.Mapping[int, character.CharacterProgression]:
 931        character_progressions: collections.Mapping[
 932            int, character.CharacterProgression
 933        ] = {}
 934        for char_id, data in payload["data"].items():
 935            # A little hack to stop mypy complaining about Mapping <-> dict
 936            character_progressions[int(char_id)] = self.deserialize_character_progressions(data)  # type: ignore[index]
 937        return character_progressions
 938
 939    def deserialize_characters_records(
 940        self,
 941        payload: typedefs.JSONObject,
 942    ) -> collections.Mapping[int, records.CharacterRecord]:
 943
 944        return {
 945            int(rec_id): self.deserialize_character_records(
 946                rec, record_hashes=payload.get("featuredRecordHashes")
 947            )
 948            for rec_id, rec in payload["records"].items()
 949        }
 950
 951    def deserialize_profile_records(
 952        self, payload: typedefs.JSONObject
 953    ) -> collections.Mapping[int, records.Record]:
 954        raw_profile_records = payload["data"]
 955        scores = records.RecordScores(
 956            current_score=raw_profile_records["score"],
 957            legacy_score=raw_profile_records["legacyScore"],
 958            lifetime_score=raw_profile_records["lifetimeScore"],
 959        )
 960        return {
 961            int(record_id): self.deserialize_records(
 962                record,
 963                scores,
 964                categories_hash=raw_profile_records["recordCategoriesRootNodeHash"],
 965                seals_hash=raw_profile_records["recordSealsRootNodeHash"],
 966            )
 967            for record_id, record in raw_profile_records["records"].items()
 968        }
 969
 970    def _deserialize_craftable_socket_plug(
 971        self, payload: typedefs.JSONObject
 972    ) -> items.CraftableSocketPlug:
 973        return items.CraftableSocketPlug(
 974            item_hash=int(payload["plugItemHash"]),
 975            failed_requirement_indexes=payload.get("failedRequirementIndexes", []),
 976        )
 977
 978    def _deserialize_craftable_socket(
 979        self, payload: typedefs.JSONObject
 980    ) -> items.CraftableSocket:
 981
 982        plugs: list[items.CraftableSocketPlug] = []
 983        if raw_plug := payload.get("plug"):
 984            plugs.extend(
 985                self._deserialize_craftable_socket_plug(plug) for plug in raw_plug
 986            )
 987
 988        return items.CraftableSocket(
 989            plug_set_hash=int(payload["plugSetHash"]), plugs=plugs
 990        )
 991
 992    def _deserialize_craftable_item(
 993        self, payload: typedefs.JSONObject
 994    ) -> items.CraftableItem:
 995
 996        return items.CraftableItem(
 997            is_visible=payload["visible"],
 998            failed_requirement_indexes=payload.get("failedRequirementIndexes", []),
 999            sockets=[
1000                self._deserialize_craftable_socket(socket)
1001                for socket in payload["sockets"]
1002            ],
1003        )
1004
1005    def deserialize_craftables_component(
1006        self, payload: typedefs.JSONObject
1007    ) -> components.CraftablesComponent:
1008        return components.CraftablesComponent(
1009            net=self._net,
1010            craftables={
1011                int(item_id): self._deserialize_craftable_item(item)
1012                for item_id, item in payload["craftables"].items()
1013                if item is not None
1014            },
1015            crafting_root_node_hash=payload["craftingRootNodeHash"],
1016        )
1017
1018    def deserialize_components(  # noqa: C901 Too complex.
1019        self, payload: typedefs.JSONObject
1020    ) -> components.Component:
1021
1022        profile_: typing.Optional[profile.Profile] = None
1023        if raw_profile := payload.get("profile"):
1024            profile_ = self.deserialize_profile(raw_profile)
1025
1026        profile_progression: typing.Optional[profile.ProfileProgression] = None
1027        if raw_profile_progression := payload.get("profileProgression"):
1028            profile_progression = self.deserialize_profile_progression(
1029                raw_profile_progression
1030            )
1031
1032        profile_currencies: typing.Optional[
1033            collections.Sequence[profile.ProfileItemImpl]
1034        ] = None
1035        if raw_profile_currencies := payload.get("profileCurrencies"):
1036            if "data" in raw_profile_currencies:
1037                profile_currencies = self.deserialize_profile_items(
1038                    raw_profile_currencies["data"]
1039                )
1040
1041        profile_inventories: typing.Optional[
1042            collections.Sequence[profile.ProfileItemImpl]
1043        ] = None
1044        if raw_profile_inventories := payload.get("profileInventory"):
1045            if "data" in raw_profile_inventories:
1046                profile_inventories = self.deserialize_profile_items(
1047                    raw_profile_inventories["data"]
1048                )
1049
1050        profile_records: typing.Optional[
1051            collections.Mapping[int, records.Record]
1052        ] = None
1053
1054        if raw_profile_records_ := payload.get("profileRecords"):
1055            profile_records = self.deserialize_profile_records(raw_profile_records_)
1056
1057        characters: typing.Optional[typing.Mapping[int, character.Character]] = None
1058        if raw_characters := payload.get("characters"):
1059            characters = self.deserialize_characters(raw_characters)
1060
1061        character_records: typing.Optional[
1062            collections.Mapping[int, records.CharacterRecord]
1063        ] = None
1064
1065        if raw_character_records := payload.get("characterRecords"):
1066            # Had to do it in two steps..
1067            to_update: typedefs.JSONObject = {}
1068            for _, data in raw_character_records["data"].items():
1069                for record_id, record in data.items():
1070                    to_update[record_id] = record
1071
1072            character_records = {
1073                int(rec_id): self.deserialize_character_records(
1074                    rec, record_hashes=to_update.get("featuredRecordHashes")
1075                )
1076                for rec_id, rec in to_update["records"].items()
1077            }
1078
1079        character_equipments: typing.Optional[
1080            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1081        ] = None
1082        if raw_character_equips := payload.get("characterEquipment"):
1083            character_equipments = self.deserialize_character_equipments(
1084                raw_character_equips
1085            )
1086
1087        character_inventories: typing.Optional[
1088            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1089        ] = None
1090        if raw_character_inventories := payload.get("characterInventories"):
1091            if "data" in raw_character_inventories:
1092                character_inventories = self.deserialize_character_equipments(
1093                    raw_character_inventories
1094                )
1095
1096        character_activities: typing.Optional[
1097            collections.Mapping[int, activity.CharacterActivity]
1098        ] = None
1099        if raw_char_acts := payload.get("characterActivities"):
1100            character_activities = self.deserialize_character_activities(raw_char_acts)
1101
1102        character_render_data: typing.Optional[
1103            collections.Mapping[int, character.RenderedData]
1104        ] = None
1105        if raw_character_render_data := payload.get("characterRenderData"):
1106            character_render_data = self.deserialize_characters_render_data(
1107                raw_character_render_data
1108            )
1109
1110        character_progressions: typing.Optional[
1111            collections.Mapping[int, character.CharacterProgression]
1112        ] = None
1113
1114        if raw_character_progressions := payload.get("characterProgressions"):
1115            character_progressions = self.deserialize_character_progressions_mapping(
1116                raw_character_progressions
1117            )
1118
1119        profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None
1120        if raw_profile_string_vars := payload.get("profileStringVariables"):
1121            profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"]
1122
1123        character_string_vars: typing.Optional[
1124            collections.Mapping[int, collections.Mapping[int, int]]
1125        ] = None
1126        if raw_character_string_vars := payload.get("characterStringVariables"):
1127            character_string_vars = {
1128                int(char_id): data["integerValuesByHash"]
1129                for char_id, data in raw_character_string_vars["data"].items()
1130            }
1131
1132        metrics: typing.Optional[
1133            collections.Sequence[
1134                collections.Mapping[
1135                    int, tuple[bool, typing.Optional[records.Objective]]
1136                ]
1137            ]
1138        ] = None
1139        root_node_hash: typing.Optional[int] = None
1140
1141        if raw_metrics := payload.get("metrics"):
1142            root_node_hash = raw_metrics["data"]["metricsRootNodeHash"]
1143            metrics = [
1144                {
1145                    int(metrics_hash): (
1146                        data["invisible"],
1147                        self.deserialize_objectives(data["objectiveProgress"])
1148                        if "objectiveProgress" in data
1149                        else None,
1150                    )
1151                    for metrics_hash, data in raw_metrics["data"]["metrics"].items()
1152                }
1153            ]
1154        transitory: typing.Optional[fireteams.FireteamParty] = None
1155        if raw_transitory := payload.get("profileTransitoryData"):
1156            if "data" in raw_transitory:
1157                transitory = self.deserialize_fireteam_party(raw_transitory["data"])
1158
1159        item_components: typing.Optional[components.ItemsComponent] = None
1160        if raw_item_components := payload.get("itemComponents"):
1161            item_components = self.deserialize_items_component(raw_item_components)
1162
1163        profile_plugsets: typing.Optional[
1164            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1165        ] = None
1166
1167        if raw_profile_plugs := payload.get("profilePlugSets"):
1168            profile_plugsets = {
1169                int(index): [self.deserialize_plug_item_state(state) for state in data]
1170                for index, data in raw_profile_plugs["data"]["plugs"].items()
1171            }
1172
1173        character_plugsets: typing.Optional[
1174            collections.Mapping[
1175                int, collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1176            ]
1177        ] = None
1178        if raw_char_plugsets := payload.get("characterPlugSets"):
1179            character_plugsets = {
1180                int(char_id): {
1181                    int(index): [
1182                        self.deserialize_plug_item_state(state) for state in data
1183                    ]
1184                    for index, data in inner["plugs"].items()
1185                }
1186                for char_id, inner in raw_char_plugsets["data"].items()
1187            }
1188
1189        character_collectibles: typing.Optional[
1190            collections.Mapping[int, items.Collectible]
1191        ] = None
1192        if raw_character_collectibles := payload.get("characterCollectibles"):
1193            character_collectibles = {
1194                int(char_id): self._deserialize_collectible(data)
1195                for char_id, data in raw_character_collectibles["data"].items()
1196            }
1197
1198        profile_collectibles: typing.Optional[items.Collectible] = None
1199        if raw_profile_collectibles := payload.get("profileCollectibles"):
1200            profile_collectibles = self._deserialize_collectible(
1201                raw_profile_collectibles["data"]
1202            )
1203
1204        profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1205        if raw_profile_nodes := payload.get("profilePresentationNodes"):
1206            profile_nodes = {
1207                int(node_hash): self._deserialize_node(node)
1208                for node_hash, node in raw_profile_nodes["data"]["nodes"].items()
1209            }
1210
1211        character_nodes: typing.Optional[
1212            collections.Mapping[int, collections.Mapping[int, records.Node]]
1213        ] = None
1214        if raw_character_nodes := payload.get("characterPresentationNodes"):
1215            character_nodes = {
1216                int(char_id): {
1217                    int(node_hash): self._deserialize_node(node)
1218                    for node_hash, node in each_character["nodes"].items()
1219                }
1220                for char_id, each_character in raw_character_nodes["data"].items()
1221            }
1222
1223        platform_silver: typing.Optional[
1224            collections.Mapping[str, profile.ProfileItemImpl]
1225        ] = None
1226        if raw_platform_silver := payload.get("platformSilver"):
1227            if "data" in raw_platform_silver:
1228                platform_silver = {
1229                    platform_name: self.deserialize_profile_item(item)
1230                    for platform_name, item in raw_platform_silver["data"][
1231                        "platformSilver"
1232                    ].items()
1233                }
1234
1235        character_currency_lookups: typing.Optional[
1236            collections.Mapping[int, collections.Sequence[items.Currency]]
1237        ] = None
1238        if raw_char_lookups := payload.get("characterCurrencyLookups"):
1239            if "data" in raw_char_lookups:
1240                character_currency_lookups = {
1241                    int(char_id): self._deserialize_currencies(currencie)
1242                    for char_id, currencie in raw_char_lookups["data"].items()
1243                }
1244
1245        character_craftables: typing.Optional[
1246            collections.Mapping[int, components.CraftablesComponent]
1247        ] = None
1248        if raw_character_craftables := payload.get("characterCraftables"):
1249
1250            if "data" in raw_character_craftables:
1251                character_craftables = {
1252                    int(char_id): self.deserialize_craftables_component(craftable)
1253                    for char_id, craftable in raw_character_craftables["data"].items()
1254                }
1255
1256        return components.Component(
1257            profiles=profile_,
1258            profile_progression=profile_progression,
1259            profile_currencies=profile_currencies,
1260            profile_inventories=profile_inventories,
1261            profile_records=profile_records,
1262            characters=characters,
1263            character_records=character_records,
1264            character_equipments=character_equipments,
1265            character_inventories=character_inventories,
1266            character_activities=character_activities,
1267            character_render_data=character_render_data,
1268            character_progressions=character_progressions,
1269            profile_string_variables=profile_string_vars,
1270            character_string_variables=character_string_vars,
1271            metrics=metrics,
1272            root_node_hash=root_node_hash,
1273            transitory=transitory,
1274            item_components=item_components,
1275            profile_plugsets=profile_plugsets,
1276            character_plugsets=character_plugsets,
1277            character_collectibles=character_collectibles,
1278            profile_collectibles=profile_collectibles,
1279            profile_nodes=profile_nodes,
1280            character_nodes=character_nodes,
1281            platform_silver=platform_silver,
1282            character_currency_lookups=character_currency_lookups,
1283            character_craftables=character_craftables,
1284        )
1285
1286    def deserialize_items_component(
1287        self, payload: typedefs.JSONObject
1288    ) -> components.ItemsComponent:
1289        instances: typing.Optional[
1290            collections.Sequence[collections.Mapping[int, items.ItemInstance]]
1291        ] = None
1292        if raw_instances := payload.get("instances"):
1293            instances = [
1294                {
1295                    int(ins_id): self.deserialize_instanced_item(item)
1296                    for ins_id, item in raw_instances["data"].items()
1297                }
1298            ]
1299
1300        render_data: typing.Optional[
1301            collections.Mapping[int, tuple[bool, dict[int, int]]]
1302        ] = None
1303        if raw_render_data := payload.get("renderData"):
1304            render_data = {
1305                int(ins_id): (data["useCustomDyes"], data["artRegions"])
1306                for ins_id, data in raw_render_data["data"].items()
1307            }
1308
1309        stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None
1310        if raw_stats := payload.get("stats"):
1311            builder: collections.Mapping[int, items.ItemStatsView] = {}
1312            for ins_id, stat in raw_stats["data"].items():
1313                for _, items_ in stat.items():
1314                    builder[int(ins_id)] = self.deserialize_item_stats_view(items_)  # type: ignore[index]
1315            stats = builder
1316
1317        sockets: typing.Optional[
1318            collections.Mapping[int, collections.Sequence[items.ItemSocket]]
1319        ] = None
1320        if raw_sockets := payload.get("sockets"):
1321            sockets = {
1322                int(ins_id): [
1323                    self.deserialize_item_socket(socket) for socket in item["sockets"]
1324                ]
1325                for ins_id, item in raw_sockets["data"].items()
1326            }
1327
1328        objeectives: typing.Optional[
1329            collections.Mapping[int, collections.Sequence[records.Objective]]
1330        ] = None
1331        if raw_objectives := payload.get("objectives"):
1332            objeectives = {
1333                int(ins_id): [self.deserialize_objectives(objective)]
1334                for ins_id, data in raw_objectives["data"].items()
1335                for objective in data["objectives"]
1336            }
1337
1338        perks: typing.Optional[
1339            collections.Mapping[int, collections.Collection[items.ItemPerk]]
1340        ] = None
1341        if raw_perks := payload.get("perks"):
1342            perks = {
1343                int(ins_id): [
1344                    self.deserialize_item_perk(perk) for perk in item["perks"]
1345                ]
1346                for ins_id, item in raw_perks["data"].items()
1347            }
1348
1349        plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None
1350        if raw_plug_states := payload.get("plugStates"):
1351            pending_states: list[items.PlugItemState] = []
1352            for _, plug in raw_plug_states["data"].items():
1353                pending_states.append(self.deserialize_plug_item_state(plug))
1354            plug_states = pending_states
1355
1356        reusable_plugs: typing.Optional[
1357            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1358        ] = None
1359        if raw_re_plugs := payload.get("reusablePlugs"):
1360            reusable_plugs = {
1361                int(ins_id): [
1362                    self.deserialize_plug_item_state(state) for state in inner
1363                ]
1364                for ins_id, plug in raw_re_plugs["data"].items()
1365                for inner in list(plug["plugs"].values())
1366            }
1367
1368        plug_objectives: typing.Optional[
1369            collections.Mapping[
1370                int, collections.Mapping[int, collections.Collection[records.Objective]]
1371            ]
1372        ] = None
1373        if raw_plug_objectives := payload.get("plugObjectives"):
1374            plug_objectives = {
1375                int(ins_id): {
1376                    int(obj_hash): [self.deserialize_objectives(obj) for obj in objs]
1377                    for obj_hash, objs in inner["objectivesPerPlug"].items()
1378                }
1379                for ins_id, inner in raw_plug_objectives["data"].items()
1380            }
1381
1382        return components.ItemsComponent(
1383            sockets=sockets,
1384            stats=stats,
1385            render_data=render_data,
1386            instances=instances,
1387            objectives=objeectives,
1388            perks=perks,
1389            plug_states=plug_states,
1390            reusable_plugs=reusable_plugs,
1391            plug_objectives=plug_objectives,
1392        )
1393
1394    def deserialize_character_component(  # type: ignore[call-arg]
1395        self, payload: typedefs.JSONObject
1396    ) -> components.CharacterComponent:
1397
1398        character_: typing.Optional[character.Character] = None
1399        if raw_singuler_character := payload.get("character"):
1400            character_ = self.deserialize_character(raw_singuler_character["data"])
1401
1402        inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1403        if raw_inventory := payload.get("inventory"):
1404            if "data" in raw_inventory:
1405                inventory = self.deserialize_profile_items(raw_inventory["data"])
1406
1407        activities: typing.Optional[activity.CharacterActivity] = None
1408        if raw_activities := payload.get("activities"):
1409            activities = self.deserialize_character_activity(raw_activities["data"])
1410
1411        equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1412        if raw_equipments := payload.get("equipment"):
1413            equipment = self.deserialize_profile_items(raw_equipments["data"])
1414
1415        progressions_: typing.Optional[character.CharacterProgression] = None
1416        if raw_progressions := payload.get("progressions"):
1417            progressions_ = self.deserialize_character_progressions(
1418                raw_progressions["data"]
1419            )
1420
1421        render_data: typing.Optional[character.RenderedData] = None
1422        if raw_render_data := payload.get("renderData"):
1423            render_data = self.deserialize_character_render_data(
1424                raw_render_data["data"]
1425            )
1426
1427        character_records: typing.Optional[
1428            collections.Mapping[int, records.CharacterRecord]
1429        ] = None
1430        if raw_char_records := payload.get("records"):
1431            character_records = self.deserialize_characters_records(
1432                raw_char_records["data"]
1433            )
1434
1435        item_components: typing.Optional[components.ItemsComponent] = None
1436        if raw_item_components := payload.get("itemComponents"):
1437            item_components = self.deserialize_items_component(raw_item_components)
1438
1439        nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1440        if raw_nodes := payload.get("presentationNodes"):
1441            nodes = {
1442                int(node_hash): self._deserialize_node(node)
1443                for node_hash, node in raw_nodes["data"]["nodes"].items()
1444            }
1445
1446        collectibles: typing.Optional[items.Collectible] = None
1447        if raw_collectibles := payload.get("collectibles"):
1448            collectibles = self._deserialize_collectible(raw_collectibles["data"])
1449
1450        currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None
1451        if raw_currencies := payload.get("currencyLookups"):
1452            if "data" in raw_currencies:
1453                currency_lookups = self._deserialize_currencies(raw_currencies)
1454
1455        return components.CharacterComponent(
1456            activities=activities,
1457            equipment=equipment,
1458            inventory=inventory,
1459            progressions=progressions_,
1460            render_data=render_data,
1461            character=character_,
1462            character_records=character_records,
1463            profile_records=None,
1464            item_components=item_components,
1465            currency_lookups=currency_lookups,
1466            collectibles=collectibles,
1467            nodes=nodes,
1468        )
1469
1470    def _set_entity_attrs(
1471        self, payload: typedefs.JSONObject, *, key: str = "displayProperties"
1472    ) -> entity.Entity:
1473
1474        name: undefined.UndefinedOr[str] = undefined.Undefined
1475        description: undefined.UndefinedOr[str] = undefined.Undefined
1476
1477        if properties := payload[key]:
1478            if (raw_name := properties["name"]) is not typedefs.Unknown:
1479                name = raw_name
1480
1481            if (
1482                raw_description := properties["description"]
1483            ) and not typedefs.is_unknown(raw_description):
1484                description = raw_description
1485
1486        return entity.Entity(
1487            net=self._net,
1488            hash=payload["hash"],
1489            index=payload["index"],
1490            name=name,
1491            description=description,
1492            has_icon=properties["hasIcon"],
1493            icon=assets.Image(properties["icon"] if "icon" in properties else None),
1494        )
1495
1496    def deserialize_inventory_results(
1497        self, payload: typedefs.JSONObject
1498    ) -> iterators.FlatIterator[entity.SearchableEntity]:
1499        suggested_words: list[str] = payload["suggestedWords"]
1500
1501        def _check_unknown(s: str) -> undefined.UndefinedOr[str]:
1502            return s if not typedefs.is_unknown(s) else undefined.Undefined
1503
1504        return iterators.FlatIterator(
1505            [
1506                entity.SearchableEntity(
1507                    net=self._net,
1508                    hash=data["hash"],
1509                    entity_type=data["entityType"],
1510                    weight=data["weight"],
1511                    suggested_words=suggested_words,
1512                    name=data["displayProperties"]["name"],
1513                    has_icon=data["displayProperties"]["hasIcon"],
1514                    description=_check_unknown(
1515                        data["displayProperties"]["description"]
1516                    ),
1517                    icon=assets.Image(data["displayProperties"]["icon"]),
1518                )
1519                for data in payload["results"]["results"]
1520            ]
1521        )
1522
1523    def _deserialize_inventory_item_objects(
1524        self, payload: typedefs.JSONObject
1525    ) -> entity.InventoryEntityObjects:
1526        return entity.InventoryEntityObjects(
1527            action=payload.get("action"),
1528            set_data=payload.get("setData"),
1529            stats=payload.get("stats"),
1530            equipping_block=payload.get("equippingBlock"),
1531            translation_block=payload.get("translationBlock"),
1532            preview=payload.get("preview"),
1533            quality=payload.get("quality"),
1534            value=payload.get("value"),
1535            source_data=payload.get("sourceData"),
1536            objectives=payload.get("objectives"),
1537            plug=payload.get("plug"),
1538            metrics=payload.get("metrics"),
1539            gearset=payload.get("gearset"),
1540            sack=payload.get("sack"),
1541            sockets=payload.get("sockets"),
1542            summary=payload.get("summary"),
1543            talent_gird=payload.get("talentGrid"),
1544            investments_stats=payload.get("investmentStats"),
1545            perks=payload.get("perks"),
1546            animations=payload.get("animations", []),
1547            links=payload.get("links", []),
1548        )
1549
1550    def deserialize_inventory_entity(  # noqa: C901 Too complex.
1551        self, payload: typedefs.JSONObject, /
1552    ) -> entity.InventoryEntity:
1553
1554        props = self._set_entity_attrs(payload)
1555        objects = self._deserialize_inventory_item_objects(payload)
1556
1557        collectible_hash: typing.Optional[int] = None
1558        if raw_collectible_hash := payload.get("collectibleHash"):
1559            collectible_hash = int(raw_collectible_hash)
1560
1561        secondary_icon: undefined.UndefinedOr[assets.Image] = undefined.Undefined
1562        if raw_second_icon := payload.get("secondaryIcon"):
1563            secondary_icon = assets.Image(raw_second_icon)
1564
1565        secondary_overlay: undefined.UndefinedOr[assets.Image] = undefined.Undefined
1566        if raw_second_overlay := payload.get("secondaryOverlay"):
1567            secondary_overlay = assets.Image(raw_second_overlay)
1568
1569        secondary_special: undefined.UndefinedOr[assets.Image] = undefined.Undefined
1570        if raw_second_special := payload.get("secondarySpecial"):
1571            secondary_special = assets.Image(raw_second_special)
1572
1573        screenshot: undefined.UndefinedOr[assets.Image] = undefined.Undefined
1574        if raw_screenshot := payload.get("screenshot"):
1575            screenshot = assets.Image(raw_screenshot)
1576
1577        watermark_icon: typing.Optional[assets.Image] = None
1578        if raw_watermark_icon := payload.get("iconWatermark"):
1579            watermark_icon = assets.Image(raw_watermark_icon)
1580
1581        watermark_shelved: typing.Optional[assets.Image] = None
1582        if raw_watermark_shelved := payload.get("iconWatermarkShelved"):
1583            watermark_shelved = assets.Image(raw_watermark_shelved)
1584
1585        about: undefined.UndefinedOr[str] = undefined.Undefined
1586        if (raw_about := payload.get("flavorText")) and not typedefs.is_unknown(
1587            raw_about
1588        ):
1589            about = raw_about
1590
1591        ui_item_style: undefined.UndefinedOr[str] = undefined.Undefined
1592        if (
1593            raw_ui_style := payload.get("uiItemDisplayStyle")
1594        ) and not typedefs.is_unknown(raw_ui_style):
1595            ui_item_style = raw_ui_style
1596
1597        tier_and_name: undefined.UndefinedOr[str] = undefined.Undefined
1598        if (
1599            raw_tier_and_name := payload.get("itemTypeAndTierDisplayName")
1600        ) and not typedefs.is_unknown(raw_tier_and_name):
1601            tier_and_name = raw_tier_and_name
1602
1603        type_name: undefined.UndefinedOr[str] = undefined.Undefined
1604        if (
1605            raw_type_name := payload.get("itemTypeDisplayName")
1606        ) and not typedefs.is_unknown(raw_type_name):
1607            type_name = raw_type_name
1608
1609        display_source: undefined.UndefinedOr[str] = undefined.Undefined
1610        if (
1611            raw_display_source := payload.get("displaySource")
1612        ) and not typedefs.is_unknown(raw_display_source):
1613            display_source = raw_display_source
1614
1615        lorehash: typing.Optional[int] = None
1616        if raw_lore_hash := payload.get("loreHash"):
1617            lorehash = int(raw_lore_hash)
1618
1619        summary_hash: typing.Optional[int] = None
1620        if raw_summary_hash := payload.get("summaryItemHash"):
1621            summary_hash = raw_summary_hash
1622
1623        breaker_type_hash: typing.Optional[int] = None
1624        if raw_breaker_type_hash := payload.get("breakerTypeHash"):
1625            breaker_type_hash = int(raw_breaker_type_hash)
1626
1627        damage_types: typing.Optional[collections.Sequence[int]] = None
1628        if raw_damage_types := payload.get("damageTypes"):
1629            damage_types = [int(type_) for type_ in raw_damage_types]
1630
1631        damagetype_hashes: typing.Optional[collections.Sequence[int]] = None
1632        if raw_damagetype_hashes := payload.get("damageTypeHashes"):
1633            damagetype_hashes = [int(type_) for type_ in raw_damagetype_hashes]
1634
1635        default_damagetype_hash: typing.Optional[int] = None
1636        if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"):
1637            default_damagetype_hash = int(raw_defaultdmg_hash)
1638
1639        emblem_objective_hash: typing.Optional[int] = None
1640        if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"):
1641            emblem_objective_hash = int(raw_emblem_obj_hash)
1642
1643        tier_type: typing.Optional[enums.TierType] = None
1644        tier: typing.Optional[enums.ItemTier] = None
1645        bucket_hash: typing.Optional[int] = None
1646        recovery_hash: typing.Optional[int] = None
1647        tier_name: undefined.UndefinedOr[str] = undefined.Undefined
1648        isinstance_item: bool = False
1649        expire_tool_tip: undefined.UndefinedOr[str] = undefined.Undefined
1650        expire_in_orbit_message: undefined.UndefinedOr[str] = undefined.Undefined
1651        suppress_expiration: bool = False
1652        max_stack_size: typing.Optional[int] = None
1653        stack_label: undefined.UndefinedOr[str] = undefined.Undefined
1654
1655        if inventory := payload.get("inventory"):
1656            tier_type = enums.TierType(int(inventory["tierType"]))
1657            tier = enums.ItemTier(int(inventory["tierTypeHash"]))
1658            bucket_hash = int(inventory["bucketTypeHash"])
1659            recovery_hash = int(inventory["recoveryBucketTypeHash"])
1660            tier_name = inventory["tierTypeName"]
1661            isinstance_item = inventory["isInstanceItem"]
1662            suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"]
1663            max_stack_size = int(inventory["maxStackSize"])
1664
1665            try:
1666                stack_label = inventory["stackUniqueLabel"]
1667            except KeyError:
1668                pass
1669
1670        return entity.InventoryEntity(
1671            net=self._net,
1672            collectible_hash=collectible_hash,
1673            name=props.name,
1674            about=about,
1675            emblem_objective_hash=emblem_objective_hash,
1676            suppress_expiration=suppress_expiration,
1677            max_stack_size=max_stack_size,
1678            stack_label=stack_label,
1679            tier=tier,
1680            tier_type=tier_type,
1681            tier_name=tier_name,
1682            bucket_hash=bucket_hash,
1683            recovery_bucket_hash=recovery_hash,
1684            isinstance_item=isinstance_item,
1685            expire_in_orbit_message=expire_in_orbit_message,
1686            expiration_tooltip=expire_tool_tip,
1687            lore_hash=lorehash,
1688            type_and_tier_name=tier_and_name,
1689            summary_hash=summary_hash,
1690            ui_display_style=ui_item_style,
1691            type_name=type_name,
1692            breaker_type_hash=breaker_type_hash,
1693            description=props.description,
1694            display_source=display_source,
1695            hash=props.hash,
1696            damage_types=damage_types,
1697            index=props.index,
1698            icon=props.icon,
1699            has_icon=props.has_icon,
1700            screenshot=screenshot,
1701            watermark_icon=watermark_icon,
1702            watermark_shelved=watermark_shelved,
1703            secondary_icon=secondary_icon,
1704            secondary_overlay=secondary_overlay,
1705            secondary_special=secondary_special,
1706            type=enums.ItemType(int(payload["itemType"])),
1707            trait_hashes=[int(id_) for id_ in payload.get("traitHashes", [])],
1708            trait_ids=[trait for trait in payload.get("traitIds", [])],
1709            category_hashes=[int(hash_) for hash_ in payload["itemCategoryHashes"]],
1710            item_class=enums.Class(int(payload["classType"])),
1711            sub_type=enums.ItemSubType(int(payload["itemSubType"])),
1712            breaker_type=int(payload["breakerType"]),
1713            default_damagetype=int(payload["defaultDamageType"]),
1714            default_damagetype_hash=default_damagetype_hash,
1715            damagetype_hashes=damagetype_hashes,
1716            tooltip_notifications=payload["tooltipNotifications"],
1717            not_transferable=payload["nonTransferrable"],
1718            allow_actions=payload["allowActions"],
1719            is_equippable=payload["equippable"],
1720            objects=objects,
1721            background_colors=payload.get("backgroundColor", {}),
1722            season_hash=payload.get("seasonHash"),
1723            has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"],
1724        )
1725
1726    def deserialize_objective_entity(
1727        self, payload: typedefs.JSONObject, /
1728    ) -> entity.ObjectiveEntity:
1729        props = self._set_entity_attrs(payload)
1730        return entity.ObjectiveEntity(
1731            net=self._net,
1732            hash=props.hash,
1733            index=props.index,
1734            description=props.description,
1735            name=props.name,
1736            has_icon=props.has_icon,
1737            icon=props.icon,
1738            unlock_value_hash=payload["unlockValueHash"],
1739            completion_value=payload["completionValue"],
1740            scope=entity.GatingScope(int(payload["scope"])),
1741            location_hash=payload["locationHash"],
1742            allowed_negative_value=payload["allowNegativeValue"],
1743            allowed_value_change=payload["allowValueChangeWhenCompleted"],
1744            counting_downward=payload["isCountingDownward"],
1745            value_style=entity.ValueUIStyle(int(payload["valueStyle"])),
1746            progress_description=payload["progressDescription"],
1747            perks=payload["perks"],
1748            stats=payload["stats"],
1749            minimum_visibility=payload["minimumVisibilityThreshold"],
1750            allow_over_completion=payload["allowOvercompletion"],
1751            show_value_style=payload["showValueOnComplete"],
1752            display_only_objective=payload["isDisplayOnlyObjective"],
1753            complete_value_style=entity.ValueUIStyle(
1754                int(payload["completedValueStyle"])
1755            ),
1756            progress_value_style=entity.ValueUIStyle(
1757                int(payload["inProgressValueStyle"])
1758            ),
1759            ui_label=payload["uiLabel"],
1760            ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])),
1761        )
1762
1763    def _deserialize_activity_values(
1764        self, payload: typedefs.JSONObject, /
1765    ) -> activity.ActivityValues:
1766        team: typing.Optional[int] = None
1767        if raw_team := payload.get("team"):
1768            team = raw_team["basic"]["value"]
1769        return activity.ActivityValues(
1770            assists=payload["assists"]["basic"]["value"],
1771            deaths=payload["deaths"]["basic"]["value"],
1772            kills=payload["kills"]["basic"]["value"],
1773            is_completed=bool(payload["completed"]["basic"]["value"]),
1774            opponents_defeated=payload["opponentsDefeated"]["basic"]["value"],
1775            efficiency=payload["efficiency"]["basic"]["value"],
1776            kd_ratio=payload["killsDeathsRatio"]["basic"]["value"],
1777            kd_assists=payload["killsDeathsAssists"]["basic"]["value"],
1778            score=payload["score"]["basic"]["value"],
1779            duration=payload["activityDurationSeconds"]["basic"]["displayValue"],
1780            team=team,
1781            completion_reason=payload["completionReason"]["basic"]["displayValue"],
1782            fireteam_id=payload["fireteamId"]["basic"]["value"],
1783            start_seconds=payload["startSeconds"]["basic"]["value"],
1784            played_time=payload["timePlayedSeconds"]["basic"]["displayValue"],
1785            player_count=payload["playerCount"]["basic"]["value"],
1786            team_score=payload["teamScore"]["basic"]["value"],
1787        )
1788
1789    def deserialize_activity(
1790        self,
1791        payload: typedefs.JSONObject,
1792        /,
1793    ) -> activity.Activity:
1794        period = time.clean_date(payload["period"])
1795        details = payload["activityDetails"]
1796        ref_id = int(details["referenceId"])
1797        instance_id = int(details["instanceId"])
1798        mode = enums.GameMode(details["mode"])
1799        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1800        is_private = details["isPrivate"]
1801        membership_type = enums.MembershipType(int(details["membershipType"]))
1802
1803        # Since we're using the same fields for post activity method
1804        # this check is required since post activity doesn't values values
1805        values = self._deserialize_activity_values(payload["values"])
1806
1807        return activity.Activity(
1808            net=self._net,
1809            hash=ref_id,
1810            instance_id=instance_id,
1811            mode=mode,
1812            modes=modes,
1813            is_private=is_private,
1814            membership_type=membership_type,
1815            occurred_at=period,
1816            values=values,
1817        )
1818
1819    def deserialize_activities(
1820        self, payload: typedefs.JSONObject
1821    ) -> iterators.FlatIterator[activity.Activity]:
1822        return iterators.FlatIterator(
1823            [
1824                self.deserialize_activity(activity_)
1825                for activity_ in payload["activities"]
1826            ]
1827        )
1828
1829    def deserialize_extended_weapon_values(
1830        self, payload: typedefs.JSONObject
1831    ) -> activity.ExtendedWeaponValues:
1832
1833        assists: typing.Optional[int] = None
1834        if raw_assists := payload["values"].get("uniqueWeaponAssists"):
1835            assists = raw_assists["basic"]["value"]
1836        assists_damage: typing.Optional[int] = None
1837
1838        if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"):
1839            assists_damage = raw_assists_damage["basic"]["value"]
1840
1841        return activity.ExtendedWeaponValues(
1842            reference_id=int(payload["referenceId"]),
1843            kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"],
1844            precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][
1845                "value"
1846            ],
1847            assists=assists,
1848            assists_damage=assists_damage,
1849            precision_kills_percentage=(
1850                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"],
1851                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][
1852                    "displayValue"
1853                ],
1854            ),
1855        )
1856
1857    def _deserialize_extended_values(
1858        self, payload: typedefs.JSONObject
1859    ) -> activity.ExtendedValues:
1860        weapons: typing.Optional[
1861            collections.Collection[activity.ExtendedWeaponValues]
1862        ] = None
1863
1864        if raw_weapons := payload.get("weapons"):
1865            weapons = [
1866                self.deserialize_extended_weapon_values(value) for value in raw_weapons
1867            ]
1868
1869        return activity.ExtendedValues(
1870            precision_kills=payload["values"]["precisionKills"]["basic"]["value"],
1871            grenade_kills=payload["values"]["weaponKillsGrenade"]["basic"]["value"],
1872            melee_kills=payload["values"]["weaponKillsMelee"]["basic"]["value"],
1873            super_kills=payload["values"]["weaponKillsSuper"]["basic"]["value"],
1874            ability_kills=payload["values"]["weaponKillsAbility"]["basic"]["value"],
1875            weapons=weapons,
1876        )
1877
1878    def deserialize_post_activity_player(
1879        self, payload: typedefs.JSONObject, /
1880    ) -> activity.PostActivityPlayer:
1881        player = payload["player"]
1882
1883        class_hash: typedefs.NoneOr[int] = None
1884        if (class_hash := player.get("classHash")) is not None:
1885            class_hash = class_hash
1886
1887        race_hash: typedefs.NoneOr[int] = None
1888        if (race_hash := player.get("raceHash")) is not None:
1889            race_hash = race_hash
1890
1891        gender_hash: typedefs.NoneOr[int] = None
1892        if (gender_hash := player.get("genderHash")) is not None:
1893            gender_hash = gender_hash
1894
1895        character_class: undefined.UndefinedOr[str] = undefined.Undefined
1896        if (
1897            character_class := player.get("characterClass")
1898        ) and not typedefs.is_unknown(character_class):
1899            character_class = character_class
1900
1901        character_level: typedefs.NoneOr[int] = None
1902        if (character_level := player.get("characterLevel")) is not None:
1903            character_level = character_level
1904
1905        return activity.PostActivityPlayer(
1906            standing=int(payload["standing"]),
1907            score=int(payload["score"]["basic"]["value"]),
1908            character_id=payload["characterId"],
1909            destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]),
1910            character_class=character_class,
1911            character_level=character_level,
1912            race_hash=race_hash,
1913            gender_hash=gender_hash,
1914            class_hash=class_hash,
1915            light_level=int(player["lightLevel"]),
1916            emblem_hash=int(player["emblemHash"]),
1917            values=self._deserialize_activity_values(payload["values"]),
1918            extended_values=self._deserialize_extended_values(payload["extended"]),
1919        )
1920
1921    def _deserialize_post_activity_team(
1922        self, payload: typedefs.JSONObject
1923    ) -> activity.PostActivityTeam:
1924        return activity.PostActivityTeam(
1925            id=payload["teamId"],
1926            is_defeated=bool(payload["standing"]["basic"]["value"]),
1927            score=int(payload["score"]["basic"]["value"]),
1928            name=payload["teamName"],
1929        )
1930
1931    def deserialize_post_activity(
1932        self, payload: typedefs.JSONObject
1933    ) -> activity.PostActivity:
1934        period = time.clean_date(payload["period"])
1935        details = payload["activityDetails"]
1936        ref_id = int(details["referenceId"])
1937        instance_id = int(details["instanceId"])
1938        mode = enums.GameMode(details["mode"])
1939        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1940        is_private = details["isPrivate"]
1941        membership_type = enums.MembershipType(int(details["membershipType"]))
1942        return activity.PostActivity(
1943            net=self._net,
1944            hash=ref_id,
1945            membership_type=membership_type,
1946            instance_id=instance_id,
1947            mode=mode,
1948            modes=modes,
1949            is_private=is_private,
1950            occurred_at=period,
1951            starting_phase=int(payload["startingPhaseIndex"]),
1952            players=[
1953                self.deserialize_post_activity_player(player)
1954                for player in payload["entries"]
1955            ],
1956            teams=[
1957                self._deserialize_post_activity_team(team) for team in payload["teams"]
1958            ],
1959        )
1960
1961    def _deserialize_aggregated_activity_values(
1962        self, payload: typedefs.JSONObject
1963    ) -> activity.AggregatedActivityValues:
1964        # This ID is always the same for all aggregated values.
1965        activity_id = int(payload["fastestCompletionMsForActivity"]["activityId"])
1966
1967        return activity.AggregatedActivityValues(
1968            id=activity_id,
1969            fastest_completion_time=(
1970                int(payload["fastestCompletionMsForActivity"]["basic"]["value"]),
1971                payload["fastestCompletionMsForActivity"]["basic"]["displayValue"],
1972            ),
1973            completions=int(payload["activityCompletions"]["basic"]["value"]),
1974            kills=int(payload["activityKills"]["basic"]["value"]),
1975            deaths=int(payload["activityDeaths"]["basic"]["value"]),
1976            assists=int(payload["activityAssists"]["basic"]["value"]),
1977            seconds_played=(
1978                int(payload["activitySecondsPlayed"]["basic"]["value"]),
1979                payload["activitySecondsPlayed"]["basic"]["displayValue"],
1980            ),
1981            wins=int(payload["activityWins"]["basic"]["value"]),
1982            goals_missed=int(payload["activityGoalsMissed"]["basic"]["value"]),
1983            special_actions=int(payload["activitySpecialActions"]["basic"]["value"]),
1984            best_goals_hit=int(payload["activityBestGoalsHit"]["basic"]["value"]),
1985            best_single_score=int(
1986                payload["activityBestSingleGameScore"]["basic"]["value"]
1987            ),
1988            goals_hit=int(payload["activityGoalsHit"]["basic"]["value"]),
1989            special_score=int(payload["activitySpecialScore"]["basic"]["value"]),
1990            kd_assists=int(payload["activityKillsDeathsAssists"]["basic"]["value"]),
1991            kd_ratio=float(
1992                payload["activityKillsDeathsAssists"]["basic"]["displayValue"]
1993            ),
1994            precision_kills=int(payload["activityPrecisionKills"]["basic"]["value"]),
1995        )
1996
1997    def deserialize_aggregated_activity(
1998        self, payload: typedefs.JSONObject
1999    ) -> activity.AggregatedActivity:
2000        return activity.AggregatedActivity(
2001            hash=int(payload["activityHash"]),
2002            values=self._deserialize_aggregated_activity_values(payload["values"]),
2003        )
2004
2005    def deserialize_aggregated_activities(
2006        self, payload: typedefs.JSONObject
2007    ) -> iterators.FlatIterator[activity.AggregatedActivity]:
2008        return iterators.FlatIterator(
2009            [
2010                self.deserialize_aggregated_activity(activity)
2011                for activity in payload["activities"]
2012            ]
2013        )
2014
2015    def deserialize_linked_profiles(
2016        self, payload: typedefs.JSONObject
2017    ) -> profile.LinkedProfile:
2018        bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"])
2019        error_profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2020        profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2021
2022        if raw_profile := payload.get("profiles"):
2023            for pfile in raw_profile:
2024                profiles_vec.append(self.deserialize_destiny_membership(pfile))
2025
2026        if raw_profiles_with_errors := payload.get("profilesWithErrors"):
2027            for raw_error_pfile in raw_profiles_with_errors:
2028                if error_pfile := raw_error_pfile.get("infoCard"):
2029                    error_profiles_vec.append(
2030                        self.deserialize_destiny_membership(error_pfile)
2031                    )
2032
2033        return profile.LinkedProfile(
2034            net=self._net,
2035            bungie=bungie_user,
2036            profiles=profiles_vec,
2037            profiles_with_errors=error_profiles_vec,
2038        )
2039
2040    def deserialize_clan_banners(
2041        self, payload: typedefs.JSONObject
2042    ) -> collections.Sequence[clans.ClanBanner]:
2043        banners_seq: typing.MutableSequence[clans.ClanBanner] = []
2044        if banners := payload.get("clanBannerDecals"):
2045            for k, v in banners.items():
2046                banner_obj = clans.ClanBanner(
2047                    id=int(k),
2048                    foreground=assets.Image(v["foregroundPath"]),
2049                    background=assets.Image(v["backgroundPath"]),
2050                )
2051                banners_seq.append(banner_obj)
2052        return banners_seq
2053
2054    def deserialize_public_milestone_content(
2055        self, payload: typedefs.JSONObject
2056    ) -> milestones.MilestoneContent:
2057        items_categoris: typedefs.NoneOr[milestones.MilestoneItems] = None
2058        if raw_categories := payload.get("itemCategories"):
2059            for item in raw_categories:
2060                title = undefined.Undefined
2061                if raw_title := item.get("title"):
2062                    if raw_title != typedefs.Unknown:
2063                        title = raw_title
2064                if raw_hashes := item.get("itemHashes"):
2065                    hashes: collections.Sequence[int] = raw_hashes
2066
2067                items_categoris = milestones.MilestoneItems(title=title, hashes=hashes)
2068
2069        about = undefined.Undefined
2070        if (raw_about := payload["about"]) != typedefs.Unknown:
2071            about = raw_about
2072
2073        status = undefined.Undefined
2074        if (raw_status := payload["status"]) != typedefs.Unknown:
2075            status = raw_status
2076
2077        tips: typing.MutableSequence[undefined.UndefinedOr[str]] = []
2078        if raw_tips := payload.get("tips"):
2079            for raw_tip in raw_tips:
2080                if raw_tip == typedefs.Unknown:
2081                    raw_tip = undefined.Undefined
2082                tips.append(raw_tip)
2083
2084        return milestones.MilestoneContent(
2085            about=about, status=status, tips=tips, items=items_categoris
2086        )
2087
2088    def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend:
2089        name = undefined.Undefined
2090        if (raw_name := payload["bungieGlobalDisplayName"]) != typedefs.Unknown:
2091            name = raw_name
2092
2093        bungie_user: typedefs.NoneOr[user.BungieUser] = None
2094
2095        if raw_bungie_user := payload.get("bungieNetUser"):
2096            bungie_user = self.deserialize_bungie_user(raw_bungie_user)
2097
2098        return friends.Friend(
2099            net=self._net,
2100            id=int(payload["lastSeenAsMembershipId"]),
2101            name=name,
2102            code=payload.get("bungieGlobalDisplayNameCode"),
2103            relationship=enums.Relationship(payload["relationship"]),
2104            user=bungie_user,
2105            online_status=enums.Presence(payload["onlineStatus"]),
2106            online_title=payload["onlineTitle"],
2107            type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]),
2108        )
2109
2110    def deserialize_friends(
2111        self, payload: typedefs.JSONObject
2112    ) -> collections.Sequence[friends.Friend]:
2113        mut_seq: typing.MutableSequence[friends.Friend] = []
2114        if raw_friends := payload.get("friends"):
2115            for friend in raw_friends:
2116                mut_seq.append(self.deserialize_friend(friend))
2117        return mut_seq
2118
2119    def deserialize_friend_requests(
2120        self, payload: typedefs.JSONObject
2121    ) -> friends.FriendRequestView:
2122        incoming: typing.MutableSequence[friends.Friend] = []
2123        outgoing: typing.MutableSequence[friends.Friend] = []
2124
2125        if raw_incoming_requests := payload.get("incomingRequests"):
2126            for incoming_request in raw_incoming_requests:
2127                incoming.append(self.deserialize_friend(incoming_request))
2128
2129        if raw_outgoing_requests := payload.get("outgoingRequests"):
2130            for outgoing_request in raw_outgoing_requests:
2131                outgoing.append(self.deserialize_friend(outgoing_request))
2132
2133        return friends.FriendRequestView(incoming=incoming, outgoing=outgoing)
2134
2135    def _set_fireteam_fields(
2136        self, payload: typedefs.JSONObject, total_results: typing.Optional[int] = None
2137    ) -> fireteams.Fireteam:
2138        activity_type = fireteams.FireteamActivity(payload["activityType"])
2139        return fireteams.Fireteam(
2140            id=int(payload["fireteamId"]),
2141            group_id=int(payload["groupId"]),
2142            platform=fireteams.FireteamPlatform(payload["platform"]),
2143            is_immediate=payload["isImmediate"],
2144            activity_type=activity_type,
2145            owner_id=int(payload["ownerMembershipId"]),
2146            player_slot_count=payload["playerSlotCount"],
2147            available_player_slots=payload["availablePlayerSlotCount"],
2148            available_alternate_slots=payload["availableAlternateSlotCount"],
2149            title=payload["title"],
2150            date_created=time.clean_date(payload["dateCreated"]),
2151            is_public=payload["isPublic"],
2152            locale=fireteams.FireteamLanguage(payload["locale"]),
2153            is_valid=payload["isValid"],
2154            last_modified=time.clean_date(payload["datePlayerModified"]),
2155            total_results=total_results or 0,
2156        )
2157
2158    def deserialize_fireteams(
2159        self, payload: typedefs.JSONObject
2160    ) -> typedefs.NoneOr[collections.Sequence[fireteams.Fireteam]]:
2161        fireteams_: typing.MutableSequence[fireteams.Fireteam] = []
2162
2163        result: list[typedefs.JSONObject]
2164        if not (result := payload["results"]):
2165            return None
2166        for elem in result:
2167            fireteams_.append(
2168                self._set_fireteam_fields(
2169                    elem, total_results=int(payload["totalResults"])
2170                )
2171            )
2172        return fireteams_
2173
2174    def deserialize_fireteam_destiny_users(
2175        self, payload: typedefs.JSONObject
2176    ) -> fireteams.FireteamUser:
2177        destiny_obj = self.deserialize_destiny_membership(payload)
2178        # We could helpers.just return a DestinyMembership object but this is
2179        # missing the fireteam display name and id fields.
2180        return fireteams.FireteamUser(
2181            net=self._net,
2182            id=destiny_obj.id,
2183            code=destiny_obj.code,
2184            icon=destiny_obj.icon,
2185            types=destiny_obj.types,
2186            type=destiny_obj.type,
2187            is_public=destiny_obj.is_public,
2188            crossave_override=destiny_obj.crossave_override,
2189            name=destiny_obj.name,
2190            last_seen_name=destiny_obj.last_seen_name,
2191            fireteam_display_name=payload["FireteamDisplayName"],
2192            fireteam_membership_id=enums.MembershipType(
2193                payload["FireteamMembershipType"]
2194            ),
2195        )
2196
2197    def deserialize_fireteam_members(
2198        self, payload: typedefs.JSONObject, *, alternatives: bool = False
2199    ) -> typing.Optional[collections.Sequence[fireteams.FireteamMember]]:
2200        members_: list[fireteams.FireteamMember] = []
2201        if members := payload.get("Members" if not alternatives else "Alternates"):
2202            for member in members:
2203                bungie_fields = self.deserialize_partial_bungie_user(member)
2204                members_fields = fireteams.FireteamMember(
2205                    destiny_user=self.deserialize_fireteam_destiny_users(member),
2206                    has_microphone=member["hasMicrophone"],
2207                    character_id=int(member["characterId"]),
2208                    date_joined=time.clean_date(member["dateJoined"]),
2209                    last_platform_invite_date=time.clean_date(
2210                        member["lastPlatformInviteAttemptDate"]
2211                    ),
2212                    last_platform_invite_result=int(
2213                        member["lastPlatformInviteAttemptResult"]
2214                    ),
2215                    net=self._net,
2216                    name=bungie_fields.name,
2217                    id=bungie_fields.id,
2218                    icon=bungie_fields.icon,
2219                    is_public=bungie_fields.is_public,
2220                    crossave_override=bungie_fields.crossave_override,
2221                    types=bungie_fields.types,
2222                    type=bungie_fields.type,
2223                )
2224                members_.append(members_fields)
2225        else:
2226            return None
2227        return members_
2228
2229    def deserialize_available_fireteams(
2230        self,
2231        data: typedefs.JSONObject,
2232        *,
2233        no_results: bool = False,
2234    ) -> typing.Union[
2235        fireteams.AvailableFireteam, collections.Sequence[fireteams.AvailableFireteam]
2236    ]:
2237        fireteams_: list[fireteams.AvailableFireteam] = []
2238
2239        # This needs to be used outside the results
2240        # JSON key.
2241        if no_results is True:
2242            payload = data
2243
2244        if result := payload.get("results"):
2245
2246            for fireteam in result:
2247                found_fireteams = self._set_fireteam_fields(fireteam["Summary"])
2248                fireteams_fields = fireteams.AvailableFireteam(
2249                    id=found_fireteams.id,
2250                    group_id=found_fireteams.group_id,
2251                    platform=found_fireteams.platform,
2252                    activity_type=found_fireteams.activity_type,
2253                    is_immediate=found_fireteams.is_immediate,
2254                    is_public=found_fireteams.is_public,
2255                    is_valid=found_fireteams.is_valid,
2256                    owner_id=found_fireteams.owner_id,
2257                    player_slot_count=found_fireteams.player_slot_count,
2258                    available_player_slots=found_fireteams.available_player_slots,
2259                    available_alternate_slots=found_fireteams.available_alternate_slots,
2260                    title=found_fireteams.title,
2261                    date_created=found_fireteams.date_created,
2262                    locale=found_fireteams.locale,
2263                    last_modified=found_fireteams.last_modified,
2264                    total_results=found_fireteams.total_results,
2265                    members=self.deserialize_fireteam_members(payload),
2266                    alternatives=self.deserialize_fireteam_members(
2267                        payload, alternatives=True
2268                    ),
2269                )
2270            fireteams_.append(fireteams_fields)
2271            if no_results:
2272                return fireteams_fields
2273        return fireteams_
2274
2275    def deserialize_fireteam_party(
2276        self, payload: typedefs.JSONObject
2277    ) -> fireteams.FireteamParty:
2278        last_destination_hash: typing.Optional[int] = None
2279        if raw_dest_hash := payload.get("lastOrbitedDestinationHash"):
2280            last_destination_hash = int(raw_dest_hash)
2281
2282        return fireteams.FireteamParty(
2283            members=[
2284                self._deserialize_fireteam_party_member(member)
2285                for member in payload["partyMembers"]
2286            ],
2287            activity=self._deserialize_fireteam_party_current_activity(
2288                payload["currentActivity"]
2289            ),
2290            settings=self._deserialize_fireteam_party_settings(payload["joinability"]),
2291            last_destination_hash=last_destination_hash,
2292            tracking=payload["tracking"],
2293        )
2294
2295    def _deserialize_fireteam_party_member(
2296        self, payload: typedefs.JSONObject
2297    ) -> fireteams.FireteamPartyMember:
2298
2299        status = fireteams.FireteamPartyMemberState(payload["status"])
2300        displayname: undefined.UndefinedOr[str] = undefined.Undefined
2301        if raw_name := payload.get("displayName"):
2302            displayname = raw_name
2303
2304        return fireteams.FireteamPartyMember(
2305            membership_id=int(payload["membershipId"]),
2306            emblem_hash=int(payload["emblemHash"]),
2307            status=status,
2308            display_name=displayname,
2309        )
2310
2311    def _deserialize_fireteam_party_current_activity(
2312        self, payload: typedefs.JSONObject
2313    ) -> fireteams.FireteamPartyCurrentActivity:
2314        start_date: typing.Optional[datetime.datetime] = None
2315        if raw_start_date := payload.get("startTime"):
2316            start_date = time.clean_date(raw_start_date)
2317
2318        end_date: typing.Optional[datetime.datetime] = None
2319        if raw_end_date := payload.get("endTime"):
2320            end_date = time.clean_date(raw_end_date)
2321        return fireteams.FireteamPartyCurrentActivity(
2322            start_time=start_date,
2323            end_time=end_date,
2324            score=float(payload["score"]),
2325            highest_opposing_score=float(payload["highestOpposingFactionScore"]),
2326            opponenst_count=int(payload["numberOfOpponents"]),
2327            player_count=int(payload["numberOfPlayers"]),
2328        )
2329
2330    def _deserialize_fireteam_party_settings(
2331        self, payload: typedefs.JSONObject
2332    ) -> fireteams.FireteamPartySettings:
2333        closed_reasons = enums.ClosedReasons(payload["closedReasons"])
2334        return fireteams.FireteamPartySettings(
2335            open_slots=int(payload["openSlots"]),
2336            privacy_setting=enums.PrivacySetting(int(payload["privacySetting"])),
2337            closed_reasons=closed_reasons,
2338        )
2339
2340    def deserialize_seasonal_artifact(
2341        self, payload: typedefs.JSONObject
2342    ) -> season.Artifact:
2343        if raw_artifact := payload.get("seasonalArtifact"):
2344            if points := raw_artifact.get("pointProgression"):
2345                points_prog = progressions.Progression(
2346                    hash=points["progressionHash"],
2347                    level=points["level"],
2348                    cap=points["levelCap"],
2349                    daily_limit=points["dailyLimit"],
2350                    weekly_limit=points["weeklyLimit"],
2351                    current_progress=points["currentProgress"],
2352                    daily_progress=points["dailyProgress"],
2353                    needed=points["progressToNextLevel"],
2354                    next_level=points["nextLevelAt"],
2355                )
2356
2357            if bonus := raw_artifact.get("powerBonusProgression"):
2358                power_bonus_prog = progressions.Progression(
2359                    hash=bonus["progressionHash"],
2360                    level=bonus["level"],
2361                    cap=bonus["levelCap"],
2362                    daily_limit=bonus["dailyLimit"],
2363                    weekly_limit=bonus["weeklyLimit"],
2364                    current_progress=bonus["currentProgress"],
2365                    daily_progress=bonus["dailyProgress"],
2366                    needed=bonus["progressToNextLevel"],
2367                    next_level=bonus["nextLevelAt"],
2368                )
2369            artifact = season.Artifact(
2370                net=self._net,
2371                hash=raw_artifact["artifactHash"],
2372                power_bonus=raw_artifact["powerBonus"],
2373                acquired_points=raw_artifact["pointsAcquired"],
2374                bonus=power_bonus_prog,
2375                points=points_prog,
2376            )
2377        return artifact
2378
2379    def deserialize_profile_progression(
2380        self, payload: typedefs.JSONObject
2381    ) -> profile.ProfileProgression:
2382        return profile.ProfileProgression(
2383            artifact=self.deserialize_seasonal_artifact(payload["data"]),
2384            checklist={
2385                int(check_id): checklists
2386                for check_id, checklists in payload["data"]["checklists"].items()
2387            },
2388        )
2389
2390    def deserialize_instanced_item(
2391        self, payload: typedefs.JSONObject
2392    ) -> items.ItemInstance:
2393        damage_type_hash: typing.Optional[int] = None
2394        if raw_damagetype_hash := payload.get("damageTypeHash"):
2395            damage_type_hash = int(raw_damagetype_hash)
2396
2397        required_hashes: typing.Optional[collections.Collection[int]] = None
2398        if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"):
2399            required_hashes = [int(raw_hash) for raw_hash in raw_required_hashes]
2400
2401        breaker_type: typing.Optional[items.ItemBreakerType] = None
2402        if raw_break_type := payload.get("breakerType"):
2403            breaker_type = items.ItemBreakerType(int(raw_break_type))
2404
2405        breaker_type_hash: typing.Optional[int] = None
2406        if raw_break_type_hash := payload.get("breakerTypeHash"):
2407            breaker_type_hash = int(raw_break_type_hash)
2408
2409        energy: typing.Optional[items.ItemEnergy] = None
2410        if raw_energy := payload.get("energy"):
2411            energy = self.deserialize_item_energy(raw_energy)
2412
2413        primary_stats = None
2414        if raw_primary_stats := payload.get("primaryStat"):
2415            primary_stats = self.deserialize_item_stats_view(raw_primary_stats)
2416
2417        return items.ItemInstance(
2418            damage_type=enums.DamageType(int(payload["damageType"])),
2419            damage_type_hash=damage_type_hash,
2420            primary_stat=primary_stats,
2421            item_level=int(payload["itemLevel"]),
2422            quality=int(payload["quality"]),
2423            is_equipped=payload["isEquipped"],
2424            can_equip=payload["canEquip"],
2425            equip_required_level=int(payload["equipRequiredLevel"]),
2426            required_equip_unlock_hashes=required_hashes,
2427            cant_equip_reason=int(payload["cannotEquipReason"]),
2428            breaker_type=breaker_type,
2429            breaker_type_hash=breaker_type_hash,
2430            energy=energy,
2431        )
2432
2433    def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy:
2434        energy_hash: typing.Optional[int] = None
2435        if raw_energy_hash := payload.get("energyTypeHash"):
2436            energy_hash = int(raw_energy_hash)
2437
2438        return items.ItemEnergy(
2439            hash=energy_hash,
2440            type=items.ItemEnergyType(int(payload["energyType"])),
2441            capacity=int(payload["energyCapacity"]),
2442            used_energy=int(payload["energyUsed"]),
2443            unused_energy=int(payload["energyUnused"]),
2444        )
2445
2446    def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk:
2447        perk_hash: typing.Optional[int] = None
2448        if raw_perk_hash := payload.get("perkHash"):
2449            perk_hash = int(raw_perk_hash)
2450
2451        return items.ItemPerk(
2452            hash=perk_hash,
2453            icon=assets.Image(payload["iconPath"]),
2454            is_active=payload["isActive"],
2455            is_visible=payload["visible"],
2456        )
2457
2458    def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket:
2459        plug_hash: typing.Optional[int] = None
2460        if raw_plug_hash := payload.get("plugHash"):
2461            plug_hash = int(raw_plug_hash)
2462
2463        enable_fail_indexes: typing.Optional[list[int]] = None
2464        if raw_indexes := payload.get("enableFailIndexes"):
2465            enable_fail_indexes = [int(index) for index in raw_indexes]
2466
2467        return items.ItemSocket(
2468            plug_hash=plug_hash,
2469            is_enabled=payload["isEnabled"],
2470            enable_fail_indexes=enable_fail_indexes,
2471            is_visible=payload.get("visible"),
2472        )
2473
2474    def deserialize_item_stats_view(
2475        self, payload: typedefs.JSONObject
2476    ) -> items.ItemStatsView:
2477        return items.ItemStatsView(
2478            stat_hash=payload.get("statHash"), value=payload.get("value")
2479        )
2480
2481    def deserialize_plug_item_state(
2482        self, payload: typedefs.JSONObject
2483    ) -> items.PlugItemState:
2484        item_hash: typing.Optional[int] = None
2485        if raw_item_hash := payload.get("plugItemHash"):
2486            item_hash = int(raw_item_hash)
2487
2488        insert_fail_indexes: typedefs.NoneOr[list[int]] = None
2489        if raw_fail_indexes := payload.get("insertFailIndexes"):
2490            insert_fail_indexes = [int(k) for k in raw_fail_indexes]
2491
2492        enable_fail_indexes: typedefs.NoneOr[list[int]] = None
2493        if raw_enabled_indexes := payload.get("enableFailIndexes"):
2494            enable_fail_indexes = [int(k) for k in raw_enabled_indexes]
2495
2496        return items.PlugItemState(
2497            item_hash=item_hash,
2498            insert_fail_indexes=insert_fail_indexes,
2499            enable_fail_indexes=enable_fail_indexes,
2500            is_enabled=payload["enabled"],
2501            can_insert=payload["canInsert"],
2502        )

The base deserialization factory class for all aiobungie objects.

Highly inspired hikari entity factory used to deserialize JSON responses from the REST client and turning them into a aiobungie.crates Python classes.

Factory(net: aiobungie.traits.Netrunner)
70    def __init__(self, net: traits.Netrunner) -> None:
71        self._net = net
def deserialize_bungie_user(self, data: dict[str, typing.Any]) -> aiobungie.crates.user.BungieUser:
73    def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser:
74        return user.BungieUser(
75            id=int(data["membershipId"]),
76            created_at=time.clean_date(data["firstAccess"]),
77            name=data.get("cachedBungieGlobalDisplayName", undefined.Undefined),
78            is_deleted=data["isDeleted"],
79            about=data["about"],
80            updated_at=time.clean_date(data["lastUpdate"]),
81            psn_name=data.get("psnDisplayName", None),
82            stadia_name=data.get("stadiaDisplayName", None),
83            steam_name=data.get("steamDisplayName", None),
84            twitch_name=data.get("twitchDisplayName", None),
85            blizzard_name=data.get("blizzardDisplayName", None),
86            status=data["statusText"],
87            locale=data["locale"],
88            picture=assets.Image(path=str(data["profilePicturePath"])),
89            code=data.get("cachedBungieGlobalDisplayNameCode", None),
90            unique_name=data.get("uniqueName", None),
91            theme_id=int(data["profileTheme"]),
92            show_activity=bool(data["showActivity"]),
93            theme_name=data["profileThemeName"],
94            display_title=data["userTitleDisplay"],
95        )

Deserialize a raw JSON Bungie.net user only payload into a user object.

This only returns the Bungie.net user and not the Destiny memberships.

Parameters
Returns
def deserialize_partial_bungie_user( self, payload: dict[str, typing.Any]) -> aiobungie.crates.user.PartialBungieUser:
 97    def deserialize_partial_bungie_user(
 98        self, payload: typedefs.JSONObject
 99    ) -> user.PartialBungieUser:
100        return user.PartialBungieUser(
101            net=self._net,
102            types=[
103                enums.MembershipType(type_)
104                for type_ in payload.get("applicableMembershipTypes", [])
105            ],
106            name=payload.get("displayName", undefined.Undefined),
107            id=int(payload["membershipId"]),
108            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
109            is_public=payload["isPublic"],
110            icon=assets.Image(payload.get("iconPath", "")),
111            type=enums.MembershipType(payload["membershipType"]),
112        )

Deserialize a raw JSON of a partial bungieNetUserInfo.

A partial user is a bungie.net user payload with missing information from the main BungieUser object.

Parameters
Returns
def deserialize_destiny_membership( self, payload: dict[str, typing.Any]) -> aiobungie.crates.user.DestinyMembership:
114    def deserialize_destiny_membership(
115        self, payload: typedefs.JSONObject
116    ) -> user.DestinyMembership:
117        name: undefined.UndefinedOr[str] = undefined.Undefined
118        if (
119            raw_name := payload.get("bungieGlobalDisplayName", "")
120        ) and not typedefs.is_unknown(raw_name):
121            name = raw_name
122
123        return user.DestinyMembership(
124            net=self._net,
125            id=int(payload["membershipId"]),
126            name=name,
127            code=payload.get("bungieGlobalDisplayNameCode", None),
128            last_seen_name=payload.get("LastSeenDisplayName")
129            or payload.get("displayName")  # noqa: W503
130            or "",  # noqa: W503
131            type=enums.MembershipType(payload["membershipType"]),
132            is_public=payload["isPublic"],
133            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
134            icon=assets.Image(payload.get("iconPath", "")),
135            types=[
136                enums.MembershipType(type_)
137                for type_ in payload.get("applicableMembershipTypes", [])
138            ],
139        )

Deserialize a raw JSON of destinyUserInfo destiny membership information.

Parameters
Returns
  • aiobungie.crates.user.DestinyMembership: A Destiny 2 membership.
def deserialize_destiny_memberships( self, data: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.user.DestinyMembership]:
141    def deserialize_destiny_memberships(
142        self, data: typedefs.JSONArray
143    ) -> collections.Sequence[user.DestinyMembership]:
144        return [self.deserialize_destiny_membership(membership) for membership in data]

Deserialize a raw JSON payload/array of destinyUserInfo.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.user.DestinyMembership]: A sequence of Destiny 2 memberships.
def deserialize_user(self, data: dict[str, typing.Any]) -> aiobungie.crates.user.User:
146    def deserialize_user(self, data: typedefs.JSONObject) -> user.User:
147
148        primary_membership_id: typing.Optional[int] = None
149        if raw_primary_id := data.get("primaryMembershipId"):
150            primary_membership_id = int(raw_primary_id)
151
152        return user.User(
153            bungie=self.deserialize_bungie_user(data["bungieNetUser"]),
154            destiny=self.deserialize_destiny_memberships(data["destinyMemberships"]),
155            primary_membership_id=primary_membership_id,
156        )

Deserialize a raw JSON results of fetched user memberships and Bungie.net user its their id.

Parameters
Returns
def deserialize_searched_user( self, payload: dict[str, typing.Any]) -> aiobungie.crates.user.SearchableDestinyUser:
158    def deserialize_searched_user(
159        self, payload: typedefs.JSONObject
160    ) -> user.SearchableDestinyUser:
161        name: undefined.UndefinedOr[str] = undefined.Undefined
162        if (raw_name := payload["bungieGlobalDisplayName"]) and not typedefs.is_unknown(
163            raw_name
164        ):
165            name = raw_name
166
167        code: typing.Optional[int] = None
168        if raw_code := payload.get("bungieGlobalDisplayNameCode"):
169            code = int(raw_code)
170
171        bungie_id: typing.Optional[int] = None
172        if raw_bungie_id := payload.get("bungieNetMembershipId"):
173            bungie_id = int(raw_bungie_id)
174
175        return user.SearchableDestinyUser(
176            name=name,
177            code=code,
178            bungie_id=bungie_id,
179            memberships=self.deserialize_destiny_memberships(
180                payload["destinyMemberships"]
181            ),
182        )

Deserialize the results of user search details.

Parameters
Returns
def deserialize_user_credentials( self, payload: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.user.UserCredentials]:
184    def deserialize_user_credentials(
185        self, payload: typedefs.JSONArray
186    ) -> collections.Sequence[user.UserCredentials]:
187        return [
188            user.UserCredentials(
189                type=enums.CredentialType(int(creds["credentialType"])),
190                display_name=creds["credentialDisplayName"],
191                is_public=creds["isPublic"],
192                self_as_string=creds.get("credentialAsString", undefined.Undefined),
193            )
194            for creds in payload
195        ]

Deserialize a JSON array of Bungie user credentials.

Parameters
Returns
@staticmethod
def set_themese_attrs( payload: list[typing.Any], /) -> Collection[aiobungie.crates.user.UserThemes]:
197    @staticmethod
198    def set_themese_attrs(
199        payload: typedefs.JSONArray, /
200    ) -> typing.Collection[user.UserThemes]:
201        return [
202            user.UserThemes(
203                id=int(entry["userThemeId"]),
204                name=entry["userThemeName"]
205                if "userThemeName" in entry
206                else undefined.Undefined,
207                description=entry["userThemeDescription"]
208                if "userThemeDescription" in entry
209                else undefined.Undefined,
210            )
211            for entry in payload
212        ]
def deserialize_user_themes( self, payload: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.user.UserThemes]:
214    def deserialize_user_themes(
215        self, payload: typedefs.JSONArray
216    ) -> collections.Sequence[user.UserThemes]:
217        return list(self.set_themese_attrs(payload))

Deserialize a raw JSON array of Bungie user themes.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of bungie user themes.
def deserialize_clan(self, payload: dict[str, typing.Any]) -> aiobungie.crates.clans.Clan:
219    def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan:
220
221        # This is kinda redundant
222        data = payload
223
224        # This is always outside the details.
225        current_user_map: typing.Optional[
226            collections.Mapping[str, clans.ClanMember]
227        ] = None
228        if raw_current_user_map := payload.get("currentUserMemberMap"):
229            current_user_map = {
230                membership_type: self.deserialize_clan_member(membership)
231                for membership_type, membership in raw_current_user_map.items()
232            }
233
234        try:
235            data = payload["detail"]
236        except KeyError:
237            pass
238
239        id = data["groupId"]
240        name = data["name"]
241        created_at = data["creationDate"]
242        member_count = data["memberCount"]
243        about = data["about"]
244        motto = data["motto"]
245        is_public = data["isPublic"]
246        banner = assets.Image(str(data["bannerPath"]))
247        avatar = assets.Image(str(data["avatarPath"]))
248        tags = data["tags"]
249        type = data["groupType"]
250
251        features = data["features"]
252        features_obj = clans.ClanFeatures(
253            max_members=features["maximumMembers"],
254            max_membership_types=features["maximumMembershipsOfGroupType"],
255            capabilities=features["capabilities"],
256            membership_types=features["membershipTypes"],
257            invite_permissions=features["invitePermissionOverride"],
258            update_banner_permissions=features["updateBannerPermissionOverride"],
259            update_culture_permissions=features["updateCulturePermissionOverride"],
260            join_level=features["joinLevel"],
261        )
262
263        information: typedefs.JSONObject = data["clanInfo"]
264        progression: collections.Mapping[int, progressions.Progression] = {
265            int(prog_hash): self.deserialize_progressions(prog)
266            for prog_hash, prog in information["d2ClanProgressions"].items()
267        }
268
269        founder: typedefs.NoneOr[clans.ClanMember] = None
270        if raw_founder := payload.get("founder"):
271            founder = self.deserialize_clan_member(raw_founder)
272
273        return clans.Clan(
274            net=self._net,
275            id=int(id),
276            name=name,
277            type=enums.GroupType(type),
278            created_at=time.clean_date(created_at),
279            member_count=member_count,
280            motto=motto,
281            about=about,
282            is_public=is_public,
283            banner=banner,
284            avatar=avatar,
285            tags=tags,
286            features=features_obj,
287            owner=founder,
288            progressions=progression,
289            call_sign=information["clanCallsign"],
290            banner_data=information["clanBannerData"],
291            chat_security=data["chatSecurity"],
292            conversation_id=int(data["conversationId"]),
293            allow_chat=data["allowChat"],
294            theme=data["theme"],
295            current_user_membership=current_user_map,
296        )

Deserialize a raw JSON payload of Bungie clan information.

Parameters
Returns
def deserialize_clan_member( self, data: dict[str, typing.Any], /) -> aiobungie.crates.clans.ClanMember:
298    def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember:
299        destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"])
300        return clans.ClanMember(
301            net=self._net,
302            last_seen_name=destiny_user.last_seen_name,
303            id=destiny_user.id,
304            name=destiny_user.name,
305            icon=destiny_user.icon,
306            last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])),
307            group_id=int(data["groupId"]),
308            joined_at=time.clean_date(data["joinDate"]),
309            types=destiny_user.types,
310            is_public=destiny_user.is_public,
311            type=destiny_user.type,
312            code=destiny_user.code,
313            is_online=data["isOnline"],
314            crossave_override=destiny_user.crossave_override,
315            bungie=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"])
316            if "bungieNetUserInfo" in data
317            else None,
318            member_type=enums.ClanMemberType(int(data["memberType"])),
319        )

Deserialize a JSON payload of a clan member information.

Parameters
Returns
def deserialize_clan_members( self, data: dict[str, typing.Any], /) -> aiobungie.FlatIterator[aiobungie.crates.clans.ClanMember]:
321    def deserialize_clan_members(
322        self, data: typedefs.JSONObject, /
323    ) -> iterators.FlatIterator[clans.ClanMember]:
324        return iterators.FlatIterator(
325            [self.deserialize_clan_member(member) for member in data["results"]]
326        )

Deserialize a JSON payload of a clan members information.

Parameters
Returns
def deserialize_group_member( self, payload: dict[str, typing.Any]) -> aiobungie.crates.clans.GroupMember:
328    def deserialize_group_member(
329        self, payload: typedefs.JSONObject
330    ) -> clans.GroupMember:
331        member = payload["member"]
332        return clans.GroupMember(
333            net=self._net,
334            join_date=time.clean_date(member["joinDate"]),
335            group_id=int(member["groupId"]),
336            member_type=enums.ClanMemberType(member["memberType"]),
337            is_online=member["isOnline"],
338            last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])),
339            inactive_memberships=payload.get("areAllMembershipsInactive", None),
340            member=self.deserialize_destiny_membership(member["destinyUserInfo"]),
341            group=self.deserialize_clan(payload["group"]),
342        )

Deserialize a JSON payload of group information for a member.

Parameters
Returns
def deserialize_clan_conversations( self, payload: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.clans.ClanConversation]:
360    def deserialize_clan_conversations(
361        self, payload: typedefs.JSONArray
362    ) -> collections.Sequence[clans.ClanConversation]:
363        return [self._deserialize_clan_conversation(conv) for conv in payload]

Deserialize a JSON array of a clan conversations information.

Parameters
Returns
def deserialize_app_owner( self, payload: dict[str, typing.Any]) -> aiobungie.crates.application.ApplicationOwner:
365    def deserialize_app_owner(
366        self, payload: typedefs.JSONObject
367    ) -> application.ApplicationOwner:
368        return application.ApplicationOwner(
369            net=self._net,
370            name=payload.get("bungieGlobalDisplayName", undefined.Undefined),
371            id=int(payload["membershipId"]),
372            type=enums.MembershipType(payload["membershipType"]),
373            icon=assets.Image(str(payload["iconPath"])),
374            is_public=payload["isPublic"],
375            code=payload.get("bungieGlobalDisplayNameCode", None),
376        )

Deserialize a JSON payload of Bungie Developer portal application owner information.

Parameters
Returns
  • aiobungie.crates.application.ApplicationOwner: An application owner.
def deserialize_app( self, payload: dict[str, typing.Any]) -> aiobungie.crates.application.Application:
378    def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application:
379        return application.Application(
380            id=int(payload["applicationId"]),
381            name=payload["name"],
382            link=payload["link"],
383            status=payload["status"],
384            redirect_url=payload.get("redirectUrl", None),
385            created_at=time.clean_date(str(payload["creationDate"])),
386            published_at=time.clean_date(str(payload["firstPublished"])),
387            owner=self.deserialize_app_owner(payload["team"][0]["user"]),  # type: ignore
388            scope=payload.get("scope", undefined.Undefined),
389        )

Deserialize a JSON payload of Bungie Developer portal application information.

Parameters
Returns
  • aiobungie.crates.application.Application: An application.
def deserialize_profile( self, payload: dict[str, typing.Any], /) -> Optional[aiobungie.crates.profile.Profile]:
412    def deserialize_profile(
413        self, payload: typedefs.JSONObject, /
414    ) -> typing.Optional[profile.Profile]:
415        if (raw_profile := payload.get("data")) is None:
416            return None
417
418        payload = raw_profile
419        id = int(payload["userInfo"]["membershipId"])
420        name = payload["userInfo"]["displayName"]
421        is_public = payload["userInfo"]["isPublic"]
422        type = enums.MembershipType(payload["userInfo"]["membershipType"])
423        last_played = time.clean_date(str(payload["dateLastPlayed"]))
424        character_ids = [int(cid) for cid in payload["characterIds"]]
425        power_cap = payload["currentSeasonRewardPowerCap"]
426
427        return profile.Profile(
428            id=int(id),
429            name=name,
430            is_public=is_public,
431            type=type,
432            last_played=last_played,
433            character_ids=character_ids,
434            power_cap=power_cap,
435            net=self._net,
436        )

Deserialize a JSON payload of Bungie.net profile information.

Parameters
Returns
def deserialize_profile_item( self, payload: dict[str, typing.Any]) -> aiobungie.crates.profile.ProfileItemImpl:
438    def deserialize_profile_item(
439        self, payload: typedefs.JSONObject
440    ) -> profile.ProfileItemImpl:
441
442        instance_id: typing.Optional[int] = None
443        if raw_instance_id := payload.get("itemInstanceId"):
444            instance_id = int(raw_instance_id)
445
446        version_number: typing.Optional[int] = None
447        if raw_version := payload.get("versionNumber"):
448            version_number = int(raw_version)
449
450        transfer_status = enums.TransferStatus(payload["transferStatus"])
451
452        return profile.ProfileItemImpl(
453            net=self._net,
454            hash=payload["itemHash"],
455            quantity=payload["quantity"],
456            bind_status=enums.ItemBindStatus(payload["bindStatus"]),
457            location=enums.ItemLocation(payload["location"]),
458            bucket=payload["bucketHash"],
459            transfer_status=transfer_status,
460            lockable=payload["lockable"],
461            state=enums.ItemState(payload["state"]),
462            dismantel_permissions=payload["dismantlePermission"],
463            is_wrapper=payload["isWrapper"],
464            instance_id=instance_id,
465            version_number=version_number,
466            ornament_id=payload.get("overrideStyleItemHash"),
467        )

Deserialize a JSON payload of a singular profile component item.

Parameters
Returns
def deserialize_objectives( self, payload: dict[str, typing.Any]) -> aiobungie.crates.records.Objective:
469    def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective:
470        return records.Objective(
471            net=self._net,
472            hash=payload["objectiveHash"],
473            visible=payload["visible"],
474            complete=payload["complete"],
475            completion_value=payload["completionValue"],
476            progress=payload.get("progress"),
477            destination_hash=payload.get("destinationHash"),
478            activity_hash=payload.get("activityHash"),
479        )

Deserialize a JSON payload of an objective found in a record profile component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
  • aiobungie.crates.records.Objective: A record objective object.
def deserialize_records( self, payload: dict[str, typing.Any], scores: Optional[aiobungie.crates.records.RecordScores] = None, **nodes: int) -> aiobungie.crates.records.Record:
481    def deserialize_records(
482        self,
483        payload: typedefs.JSONObject,
484        scores: typing.Optional[records.RecordScores] = None,
485        **nodes: int,
486    ) -> records.Record:
487        objectives: typing.Optional[list[records.Objective]] = None
488        interval_objectives: typing.Optional[list[records.Objective]] = None
489        record_state: typedefs.IntAnd[records.RecordState]
490
491        record_state = records.RecordState(payload["state"])
492
493        if raw_objs := payload.get("objectives"):
494            objectives = [self.deserialize_objectives(obj) for obj in raw_objs]
495
496        if raw_interval_objs := payload.get("intervalObjectives"):
497            interval_objectives = [
498                self.deserialize_objectives(obj) for obj in raw_interval_objs
499            ]
500
501        return records.Record(
502            scores=scores,
503            categories_node_hash=nodes.get("categories_hash", undefined.Undefined),
504            seals_node_hash=nodes.get("seals_hash", undefined.Undefined),
505            state=record_state,
506            objectives=objectives,
507            interval_objectives=interval_objectives,
508            redeemed_count=payload.get("intervalsRedeemedCount", 0),
509            completion_times=payload.get("completedCount", None),
510            reward_visibility=payload.get("rewardVisibilty", None),
511        )

Deserialize a JSON object of a profile record component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON object payload
  • scores (typing.Optional[records.RecordScores]): The records scores object. This exists only to keep the signature of aiobungie.crates.CharacterRecord with the record object. As it will always be None in that object.
  • **nodes (int): An int kwargs use to grab the node hashes while deserializing components.
Returns
  • aiobungie.records.Record: A standard implementation of a profile record component.
def deserialize_character_records( self, payload: dict[str, typing.Any], scores: Optional[aiobungie.crates.records.RecordScores] = None, record_hashes: Optional[list[int]] = None) -> aiobungie.crates.records.CharacterRecord:
513    def deserialize_character_records(
514        self,
515        payload: typedefs.JSONObject,
516        scores: typing.Optional[records.RecordScores] = None,
517        record_hashes: typing.Optional[list[int]] = None,
518    ) -> records.CharacterRecord:
519
520        record = self.deserialize_records(payload, scores)
521        return records.CharacterRecord(
522            scores=scores,
523            categories_node_hash=record.categories_node_hash,
524            seals_node_hash=record.seals_node_hash,
525            state=record.state,
526            objectives=record.objectives,
527            interval_objectives=record.interval_objectives,
528            redeemed_count=payload.get("intervalsRedeemedCount", 0),
529            completion_times=payload.get("completedCount"),
530            reward_visibility=payload.get("rewardVisibilty"),
531            record_hashes=record_hashes or [],
532        )

Deserialize a JSON object of a profile character record component.

This almost does the same this as deserialize_records but has more fields which can only be found in a character record.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON object payload
Returns
  • aiobungie.records.CharacterRecord: A standard implementation of a profile character record component.
def deserialize_character_dye(self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.Dye:
534    def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye:
535        return character.Dye(
536            channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"]
537        )

Deserialize a JSON payload of a character's dye information.

Parameters
Returns
  • aiobungie.crates.character.Dye: Information about a character dye object.
def deserialize_character_customization( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.CustomizationOptions:
539    def deserialize_character_customization(
540        self, payload: typedefs.JSONObject
541    ) -> character.CustomizationOptions:
542        return character.CustomizationOptions(
543            personality=payload["personality"],
544            face=payload["face"],
545            skin_color=payload["skinColor"],
546            lip_color=payload["lipColor"],
547            eye_color=payload["eyeColor"],
548            hair_colors=payload.get("hairColors", []),
549            feature_colors=payload.get("featureColors", []),
550            decal_color=payload["decalColor"],
551            wear_helmet=payload["wearHelmet"],
552            hair_index=payload["hairIndex"],
553            feature_index=payload["featureIndex"],
554            decal_index=payload["decalIndex"],
555        )

Deserialize a JSON payload of a character customization information found in character render data profile component.

Parameters
Returns
  • aiobungie.crates.character.CustomizationOptions: Information about a character customs object.
def deserialize_character_minimal_equipments( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.MinimalEquipments:
557    def deserialize_character_minimal_equipments(
558        self, payload: typedefs.JSONObject
559    ) -> character.MinimalEquipments:
560        dyes = None
561        if raw_dyes := payload.get("dyes"):
562            if raw_dyes:
563                dyes = [self.deserialize_character_dye(dye) for dye in raw_dyes]
564        return character.MinimalEquipments(
565            net=self._net, item_hash=payload["itemHash"], dyes=dyes
566        )

Deserialize a singular JSON peer view of equipment found in character render data profile component.

Parameters
Returns
  • aiobungie.crates.character.MinimalEquipments: A minimal equipment object.
def deserialize_character_render_data( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.character.RenderedData:
568    def deserialize_character_render_data(
569        self, payload: typedefs.JSONObject, /
570    ) -> character.RenderedData:
571        return character.RenderedData(
572            net=self._net,
573            customization=self.deserialize_character_customization(
574                payload["customization"]
575            ),
576            custom_dyes=[
577                self.deserialize_character_dye(dye)
578                for dye in payload["customDyes"]
579                if dye
580            ],
581            equipment=[
582                self.deserialize_character_minimal_equipments(equipment)
583                for equipment in payload["peerView"]["equipment"]
584            ],
585        )

Deserialize a JSON payload of a profile character render data component.

Parameters
Returns
def deserialize_available_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.AvailableActivity:
587    def deserialize_available_activity(
588        self, payload: typedefs.JSONObject
589    ) -> activity.AvailableActivity:
590        return activity.AvailableActivity(
591            hash=payload["activityHash"],
592            is_new=payload["isNew"],
593            is_completed=payload["isCompleted"],
594            is_visible=payload["isVisible"],
595            display_level=payload.get("displayLevel"),
596            recommended_light=payload.get("recommendedLight"),
597            difficulty=activity.Difficulty(payload["difficultyTier"]),
598            can_join=payload["canJoin"],
599            can_lead=payload["canLead"],
600        )

Deserialize a JSON payload of an available activities.

This method is used to deserialize an array of aiobungie.crates.CharacterActivity.available_activities.

Parameters
Returns
def deserialize_character_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.CharacterActivity:
602    def deserialize_character_activity(
603        self, payload: typedefs.JSONObject
604    ) -> activity.CharacterActivity:
605        current_mode: typing.Optional[enums.GameMode] = None
606        if raw_current_mode := payload.get("currentActivityModeType"):
607            current_mode = enums.GameMode(raw_current_mode)
608
609        current_mode_types: typing.Optional[collections.Sequence[enums.GameMode]] = None
610        if raw_current_modes := payload.get("currentActivityModeTypes"):
611            current_mode_types = [enums.GameMode(type_) for type_ in raw_current_modes]
612
613        return activity.CharacterActivity(
614            date_started=time.clean_date(payload["dateActivityStarted"]),
615            current_hash=payload["currentActivityHash"],
616            current_mode_hash=payload["currentActivityModeHash"],
617            current_mode=current_mode,
618            current_mode_hashes=payload.get("currentActivityModeHashes"),
619            current_mode_types=current_mode_types,
620            current_playlist_hash=payload.get("currentPlaylistActivityHash"),
621            last_story_hash=payload["lastCompletedStoryHash"],
622            available_activities=[
623                self.deserialize_available_activity(activity_)
624                for activity_ in payload["availableActivities"]
625            ],
626        )

Deserialize a JSON payload of character activity profile component.

Parameters
Returns
def deserialize_profile_items( self, payload: dict[str, typing.Any], /) -> list[aiobungie.crates.profile.ProfileItemImpl]:
628    def deserialize_profile_items(
629        self, payload: typedefs.JSONObject, /
630    ) -> list[profile.ProfileItemImpl]:
631        return [self.deserialize_profile_item(item) for item in payload["items"]]

Deserialize a JSON payload of profile items component information.

This may deserialize profileInventories or profileCurrencies or any other alternatives.

Parameters
Returns
def deserialize_progressions( self, payload: dict[str, typing.Any]) -> aiobungie.crates.progressions.Progression:
674    def deserialize_progressions(
675        self, payload: typedefs.JSONObject
676    ) -> progressions.Progression:
677        return progressions.Progression(
678            hash=int(payload["progressionHash"]),
679            level=int(payload["level"]),
680            cap=int(payload["levelCap"]),
681            daily_limit=int(payload["dailyLimit"]),
682            weekly_limit=int(payload["weeklyLimit"]),
683            current_progress=int(payload["currentProgress"]),
684            daily_progress=int(payload["dailyProgress"]),
685            needed=int(payload["progressToNextLevel"]),
686            next_level=int(payload["nextLevelAt"]),
687        )
def deserialize_milestone( self, payload: dict[str, typing.Any]) -> aiobungie.crates.milestones.Milestone:
775    def deserialize_milestone(
776        self, payload: typedefs.JSONObject
777    ) -> milestones.Milestone:
778        start_date: typing.Optional[datetime.datetime] = None
779        if raw_start_date := payload.get("startDate"):
780            start_date = time.clean_date(raw_start_date)
781
782        end_date: typing.Optional[datetime.datetime] = None
783        if raw_end_date := payload.get("endDate"):
784            end_date = time.clean_date(raw_end_date)
785
786        rewards: typing.Optional[
787            collections.Collection[milestones.MilestoneReward]
788        ] = None
789        if raw_rewards := payload.get("rewards"):
790            rewards = [
791                self._deserialize_milestone_rewards(reward) for reward in raw_rewards
792            ]
793
794        activities: typing.Optional[
795            collections.Sequence[milestones.MilestoneActivity]
796        ] = None
797        if raw_activities := payload.get("activities"):
798            activities = [
799                self._deserialize_milestone_activity(active)
800                for active in raw_activities
801            ]
802
803        quests: typing.Optional[collections.Sequence[milestones.MilestoneQuest]] = None
804        if raw_quests := payload.get("availableQuests"):
805            quests = [
806                self._deserialize_milestone_available_quest(quest)
807                for quest in raw_quests
808            ]
809
810        vendors: typing.Optional[
811            collections.Sequence[milestones.MilestoneVendor]
812        ] = None
813        if raw_vendors := payload.get("vendors"):
814            vendors = [
815                milestones.MilestoneVendor(
816                    vendor_hash=vendor["vendorHash"],
817                    preview_itemhash=vendor.get("previewItemHash"),
818                )
819                for vendor in raw_vendors
820            ]
821
822        return milestones.Milestone(
823            hash=payload["milestoneHash"],
824            start_date=start_date,
825            end_date=end_date,
826            order=payload["order"],
827            rewards=rewards,
828            available_quests=quests,
829            activities=activities,
830            vendors=vendors,
831        )
def deserialize_characters( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.Character]:
848    def deserialize_characters(
849        self, payload: typedefs.JSONObject
850    ) -> collections.Mapping[int, character.Character]:
851        return {
852            int(char_id): self._set_character_attrs(char)
853            for char_id, char in payload["data"].items()
854        }
def deserialize_character( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.Character:
856    def deserialize_character(
857        self, payload: typedefs.JSONObject
858    ) -> character.Character:
859        return self._set_character_attrs(payload)
def deserialize_character_equipments( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, collections.abc.Sequence[aiobungie.crates.profile.ProfileItemImpl]]:
861    def deserialize_character_equipments(
862        self, payload: typedefs.JSONObject
863    ) -> collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]:
864        return {
865            int(char_id): self.deserialize_profile_items(item)
866            for char_id, item in payload["data"].items()
867        }
def deserialize_character_activities( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.activity.CharacterActivity]:
869    def deserialize_character_activities(
870        self, payload: typedefs.JSONObject
871    ) -> collections.Mapping[int, activity.CharacterActivity]:
872        return {
873            int(char_id): self.deserialize_character_activity(data)
874            for char_id, data in payload["data"].items()
875        }
def deserialize_characters_render_data( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.RenderedData]:
877    def deserialize_characters_render_data(
878        self, payload: typedefs.JSONObject
879    ) -> collections.Mapping[int, character.RenderedData]:
880        return {
881            int(char_id): self.deserialize_character_render_data(data)
882            for char_id, data in payload["data"].items()
883        }
def deserialize_character_progressions( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.CharacterProgression:
885    def deserialize_character_progressions(
886        self, payload: typedefs.JSONObject
887    ) -> character.CharacterProgression:
888        progressions_ = {
889            int(prog_id): self.deserialize_progressions(prog)
890            for prog_id, prog in payload["progressions"].items()
891        }
892
893        factions = {
894            int(faction_id): self._deserialize_factions(faction)
895            for faction_id, faction in payload["factions"].items()
896        }
897
898        milestones_ = {
899            int(milestone_hash): self.deserialize_milestone(milestone)
900            for milestone_hash, milestone in payload["milestones"].items()
901        }
902
903        uninstanced_item_objectives = {
904            int(item_hash): [self.deserialize_objectives(ins) for ins in obj]
905            for item_hash, obj in payload["uninstancedItemObjectives"].items()
906        }
907
908        artifact = payload["seasonalArtifact"]
909        seasonal_artifact = season.CharacterScopedArtifact(
910            hash=artifact["artifactHash"],
911            points_used=artifact["pointsUsed"],
912            reset_count=artifact["resetCount"],
913            tiers=[
914                self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"]
915            ],
916        )
917        checklists = payload["checklists"]
918
919        return character.CharacterProgression(
920            progressions=progressions_,
921            factions=factions,
922            checklists=checklists,
923            milestones=milestones_,
924            seasonal_artifact=seasonal_artifact,
925            uninstanced_item_objectives=uninstanced_item_objectives,
926        )
def deserialize_character_progressions_mapping( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.CharacterProgression]:
928    def deserialize_character_progressions_mapping(
929        self, payload: typedefs.JSONObject
930    ) -> collections.Mapping[int, character.CharacterProgression]:
931        character_progressions: collections.Mapping[
932            int, character.CharacterProgression
933        ] = {}
934        for char_id, data in payload["data"].items():
935            # A little hack to stop mypy complaining about Mapping <-> dict
936            character_progressions[int(char_id)] = self.deserialize_character_progressions(data)  # type: ignore[index]
937        return character_progressions
def deserialize_characters_records( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.records.CharacterRecord]:
939    def deserialize_characters_records(
940        self,
941        payload: typedefs.JSONObject,
942    ) -> collections.Mapping[int, records.CharacterRecord]:
943
944        return {
945            int(rec_id): self.deserialize_character_records(
946                rec, record_hashes=payload.get("featuredRecordHashes")
947            )
948            for rec_id, rec in payload["records"].items()
949        }
def deserialize_profile_records( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.records.Record]:
951    def deserialize_profile_records(
952        self, payload: typedefs.JSONObject
953    ) -> collections.Mapping[int, records.Record]:
954        raw_profile_records = payload["data"]
955        scores = records.RecordScores(
956            current_score=raw_profile_records["score"],
957            legacy_score=raw_profile_records["legacyScore"],
958            lifetime_score=raw_profile_records["lifetimeScore"],
959        )
960        return {
961            int(record_id): self.deserialize_records(
962                record,
963                scores,
964                categories_hash=raw_profile_records["recordCategoriesRootNodeHash"],
965                seals_hash=raw_profile_records["recordSealsRootNodeHash"],
966            )
967            for record_id, record in raw_profile_records["records"].items()
968        }
def deserialize_craftables_component( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.CraftablesComponent:
1005    def deserialize_craftables_component(
1006        self, payload: typedefs.JSONObject
1007    ) -> components.CraftablesComponent:
1008        return components.CraftablesComponent(
1009            net=self._net,
1010            craftables={
1011                int(item_id): self._deserialize_craftable_item(item)
1012                for item_id, item in payload["craftables"].items()
1013                if item is not None
1014            },
1015            crafting_root_node_hash=payload["craftingRootNodeHash"],
1016        )
def deserialize_components( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.Component:
1018    def deserialize_components(  # noqa: C901 Too complex.
1019        self, payload: typedefs.JSONObject
1020    ) -> components.Component:
1021
1022        profile_: typing.Optional[profile.Profile] = None
1023        if raw_profile := payload.get("profile"):
1024            profile_ = self.deserialize_profile(raw_profile)
1025
1026        profile_progression: typing.Optional[profile.ProfileProgression] = None
1027        if raw_profile_progression := payload.get("profileProgression"):
1028            profile_progression = self.deserialize_profile_progression(
1029                raw_profile_progression
1030            )
1031
1032        profile_currencies: typing.Optional[
1033            collections.Sequence[profile.ProfileItemImpl]
1034        ] = None
1035        if raw_profile_currencies := payload.get("profileCurrencies"):
1036            if "data" in raw_profile_currencies:
1037                profile_currencies = self.deserialize_profile_items(
1038                    raw_profile_currencies["data"]
1039                )
1040
1041        profile_inventories: typing.Optional[
1042            collections.Sequence[profile.ProfileItemImpl]
1043        ] = None
1044        if raw_profile_inventories := payload.get("profileInventory"):
1045            if "data" in raw_profile_inventories:
1046                profile_inventories = self.deserialize_profile_items(
1047                    raw_profile_inventories["data"]
1048                )
1049
1050        profile_records: typing.Optional[
1051            collections.Mapping[int, records.Record]
1052        ] = None
1053
1054        if raw_profile_records_ := payload.get("profileRecords"):
1055            profile_records = self.deserialize_profile_records(raw_profile_records_)
1056
1057        characters: typing.Optional[typing.Mapping[int, character.Character]] = None
1058        if raw_characters := payload.get("characters"):
1059            characters = self.deserialize_characters(raw_characters)
1060
1061        character_records: typing.Optional[
1062            collections.Mapping[int, records.CharacterRecord]
1063        ] = None
1064
1065        if raw_character_records := payload.get("characterRecords"):
1066            # Had to do it in two steps..
1067            to_update: typedefs.JSONObject = {}
1068            for _, data in raw_character_records["data"].items():
1069                for record_id, record in data.items():
1070                    to_update[record_id] = record
1071
1072            character_records = {
1073                int(rec_id): self.deserialize_character_records(
1074                    rec, record_hashes=to_update.get("featuredRecordHashes")
1075                )
1076                for rec_id, rec in to_update["records"].items()
1077            }
1078
1079        character_equipments: typing.Optional[
1080            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1081        ] = None
1082        if raw_character_equips := payload.get("characterEquipment"):
1083            character_equipments = self.deserialize_character_equipments(
1084                raw_character_equips
1085            )
1086
1087        character_inventories: typing.Optional[
1088            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1089        ] = None
1090        if raw_character_inventories := payload.get("characterInventories"):
1091            if "data" in raw_character_inventories:
1092                character_inventories = self.deserialize_character_equipments(
1093                    raw_character_inventories
1094                )
1095
1096        character_activities: typing.Optional[
1097            collections.Mapping[int, activity.CharacterActivity]
1098        ] = None
1099        if raw_char_acts := payload.get("characterActivities"):
1100            character_activities = self.deserialize_character_activities(raw_char_acts)
1101
1102        character_render_data: typing.Optional[
1103            collections.Mapping[int, character.RenderedData]
1104        ] = None
1105        if raw_character_render_data := payload.get("characterRenderData"):
1106            character_render_data = self.deserialize_characters_render_data(
1107                raw_character_render_data
1108            )
1109
1110        character_progressions: typing.Optional[
1111            collections.Mapping[int, character.CharacterProgression]
1112        ] = None
1113
1114        if raw_character_progressions := payload.get("characterProgressions"):
1115            character_progressions = self.deserialize_character_progressions_mapping(
1116                raw_character_progressions
1117            )
1118
1119        profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None
1120        if raw_profile_string_vars := payload.get("profileStringVariables"):
1121            profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"]
1122
1123        character_string_vars: typing.Optional[
1124            collections.Mapping[int, collections.Mapping[int, int]]
1125        ] = None
1126        if raw_character_string_vars := payload.get("characterStringVariables"):
1127            character_string_vars = {
1128                int(char_id): data["integerValuesByHash"]
1129                for char_id, data in raw_character_string_vars["data"].items()
1130            }
1131
1132        metrics: typing.Optional[
1133            collections.Sequence[
1134                collections.Mapping[
1135                    int, tuple[bool, typing.Optional[records.Objective]]
1136                ]
1137            ]
1138        ] = None
1139        root_node_hash: typing.Optional[int] = None
1140
1141        if raw_metrics := payload.get("metrics"):
1142            root_node_hash = raw_metrics["data"]["metricsRootNodeHash"]
1143            metrics = [
1144                {
1145                    int(metrics_hash): (
1146                        data["invisible"],
1147                        self.deserialize_objectives(data["objectiveProgress"])
1148                        if "objectiveProgress" in data
1149                        else None,
1150                    )
1151                    for metrics_hash, data in raw_metrics["data"]["metrics"].items()
1152                }
1153            ]
1154        transitory: typing.Optional[fireteams.FireteamParty] = None
1155        if raw_transitory := payload.get("profileTransitoryData"):
1156            if "data" in raw_transitory:
1157                transitory = self.deserialize_fireteam_party(raw_transitory["data"])
1158
1159        item_components: typing.Optional[components.ItemsComponent] = None
1160        if raw_item_components := payload.get("itemComponents"):
1161            item_components = self.deserialize_items_component(raw_item_components)
1162
1163        profile_plugsets: typing.Optional[
1164            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1165        ] = None
1166
1167        if raw_profile_plugs := payload.get("profilePlugSets"):
1168            profile_plugsets = {
1169                int(index): [self.deserialize_plug_item_state(state) for state in data]
1170                for index, data in raw_profile_plugs["data"]["plugs"].items()
1171            }
1172
1173        character_plugsets: typing.Optional[
1174            collections.Mapping[
1175                int, collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1176            ]
1177        ] = None
1178        if raw_char_plugsets := payload.get("characterPlugSets"):
1179            character_plugsets = {
1180                int(char_id): {
1181                    int(index): [
1182                        self.deserialize_plug_item_state(state) for state in data
1183                    ]
1184                    for index, data in inner["plugs"].items()
1185                }
1186                for char_id, inner in raw_char_plugsets["data"].items()
1187            }
1188
1189        character_collectibles: typing.Optional[
1190            collections.Mapping[int, items.Collectible]
1191        ] = None
1192        if raw_character_collectibles := payload.get("characterCollectibles"):
1193            character_collectibles = {
1194                int(char_id): self._deserialize_collectible(data)
1195                for char_id, data in raw_character_collectibles["data"].items()
1196            }
1197
1198        profile_collectibles: typing.Optional[items.Collectible] = None
1199        if raw_profile_collectibles := payload.get("profileCollectibles"):
1200            profile_collectibles = self._deserialize_collectible(
1201                raw_profile_collectibles["data"]
1202            )
1203
1204        profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1205        if raw_profile_nodes := payload.get("profilePresentationNodes"):
1206            profile_nodes = {
1207                int(node_hash): self._deserialize_node(node)
1208                for node_hash, node in raw_profile_nodes["data"]["nodes"].items()
1209            }
1210
1211        character_nodes: typing.Optional[
1212            collections.Mapping[int, collections.Mapping[int, records.Node]]
1213        ] = None
1214        if raw_character_nodes := payload.get("characterPresentationNodes"):
1215            character_nodes = {
1216                int(char_id): {
1217                    int(node_hash): self._deserialize_node(node)
1218                    for node_hash, node in each_character["nodes"].items()
1219                }
1220                for char_id, each_character in raw_character_nodes["data"].items()
1221            }
1222
1223        platform_silver: typing.Optional[
1224            collections.Mapping[str, profile.ProfileItemImpl]
1225        ] = None
1226        if raw_platform_silver := payload.get("platformSilver"):
1227            if "data" in raw_platform_silver:
1228                platform_silver = {
1229                    platform_name: self.deserialize_profile_item(item)
1230                    for platform_name, item in raw_platform_silver["data"][
1231                        "platformSilver"
1232                    ].items()
1233                }
1234
1235        character_currency_lookups: typing.Optional[
1236            collections.Mapping[int, collections.Sequence[items.Currency]]
1237        ] = None
1238        if raw_char_lookups := payload.get("characterCurrencyLookups"):
1239            if "data" in raw_char_lookups:
1240                character_currency_lookups = {
1241                    int(char_id): self._deserialize_currencies(currencie)
1242                    for char_id, currencie in raw_char_lookups["data"].items()
1243                }
1244
1245        character_craftables: typing.Optional[
1246            collections.Mapping[int, components.CraftablesComponent]
1247        ] = None
1248        if raw_character_craftables := payload.get("characterCraftables"):
1249
1250            if "data" in raw_character_craftables:
1251                character_craftables = {
1252                    int(char_id): self.deserialize_craftables_component(craftable)
1253                    for char_id, craftable in raw_character_craftables["data"].items()
1254                }
1255
1256        return components.Component(
1257            profiles=profile_,
1258            profile_progression=profile_progression,
1259            profile_currencies=profile_currencies,
1260            profile_inventories=profile_inventories,
1261            profile_records=profile_records,
1262            characters=characters,
1263            character_records=character_records,
1264            character_equipments=character_equipments,
1265            character_inventories=character_inventories,
1266            character_activities=character_activities,
1267            character_render_data=character_render_data,
1268            character_progressions=character_progressions,
1269            profile_string_variables=profile_string_vars,
1270            character_string_variables=character_string_vars,
1271            metrics=metrics,
1272            root_node_hash=root_node_hash,
1273            transitory=transitory,
1274            item_components=item_components,
1275            profile_plugsets=profile_plugsets,
1276            character_plugsets=character_plugsets,
1277            character_collectibles=character_collectibles,
1278            profile_collectibles=profile_collectibles,
1279            profile_nodes=profile_nodes,
1280            character_nodes=character_nodes,
1281            platform_silver=platform_silver,
1282            character_currency_lookups=character_currency_lookups,
1283            character_craftables=character_craftables,
1284        )

Deserialize a JSON payload of Bungie.net profile components information.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_items_component( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.ItemsComponent:
1286    def deserialize_items_component(
1287        self, payload: typedefs.JSONObject
1288    ) -> components.ItemsComponent:
1289        instances: typing.Optional[
1290            collections.Sequence[collections.Mapping[int, items.ItemInstance]]
1291        ] = None
1292        if raw_instances := payload.get("instances"):
1293            instances = [
1294                {
1295                    int(ins_id): self.deserialize_instanced_item(item)
1296                    for ins_id, item in raw_instances["data"].items()
1297                }
1298            ]
1299
1300        render_data: typing.Optional[
1301            collections.Mapping[int, tuple[bool, dict[int, int]]]
1302        ] = None
1303        if raw_render_data := payload.get("renderData"):
1304            render_data = {
1305                int(ins_id): (data["useCustomDyes"], data["artRegions"])
1306                for ins_id, data in raw_render_data["data"].items()
1307            }
1308
1309        stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None
1310        if raw_stats := payload.get("stats"):
1311            builder: collections.Mapping[int, items.ItemStatsView] = {}
1312            for ins_id, stat in raw_stats["data"].items():
1313                for _, items_ in stat.items():
1314                    builder[int(ins_id)] = self.deserialize_item_stats_view(items_)  # type: ignore[index]
1315            stats = builder
1316
1317        sockets: typing.Optional[
1318            collections.Mapping[int, collections.Sequence[items.ItemSocket]]
1319        ] = None
1320        if raw_sockets := payload.get("sockets"):
1321            sockets = {
1322                int(ins_id): [
1323                    self.deserialize_item_socket(socket) for socket in item["sockets"]
1324                ]
1325                for ins_id, item in raw_sockets["data"].items()
1326            }
1327
1328        objeectives: typing.Optional[
1329            collections.Mapping[int, collections.Sequence[records.Objective]]
1330        ] = None
1331        if raw_objectives := payload.get("objectives"):
1332            objeectives = {
1333                int(ins_id): [self.deserialize_objectives(objective)]
1334                for ins_id, data in raw_objectives["data"].items()
1335                for objective in data["objectives"]
1336            }
1337
1338        perks: typing.Optional[
1339            collections.Mapping[int, collections.Collection[items.ItemPerk]]
1340        ] = None
1341        if raw_perks := payload.get("perks"):
1342            perks = {
1343                int(ins_id): [
1344                    self.deserialize_item_perk(perk) for perk in item["perks"]
1345                ]
1346                for ins_id, item in raw_perks["data"].items()
1347            }
1348
1349        plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None
1350        if raw_plug_states := payload.get("plugStates"):
1351            pending_states: list[items.PlugItemState] = []
1352            for _, plug in raw_plug_states["data"].items():
1353                pending_states.append(self.deserialize_plug_item_state(plug))
1354            plug_states = pending_states
1355
1356        reusable_plugs: typing.Optional[
1357            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1358        ] = None
1359        if raw_re_plugs := payload.get("reusablePlugs"):
1360            reusable_plugs = {
1361                int(ins_id): [
1362                    self.deserialize_plug_item_state(state) for state in inner
1363                ]
1364                for ins_id, plug in raw_re_plugs["data"].items()
1365                for inner in list(plug["plugs"].values())
1366            }
1367
1368        plug_objectives: typing.Optional[
1369            collections.Mapping[
1370                int, collections.Mapping[int, collections.Collection[records.Objective]]
1371            ]
1372        ] = None
1373        if raw_plug_objectives := payload.get("plugObjectives"):
1374            plug_objectives = {
1375                int(ins_id): {
1376                    int(obj_hash): [self.deserialize_objectives(obj) for obj in objs]
1377                    for obj_hash, objs in inner["objectivesPerPlug"].items()
1378                }
1379                for ins_id, inner in raw_plug_objectives["data"].items()
1380            }
1381
1382        return components.ItemsComponent(
1383            sockets=sockets,
1384            stats=stats,
1385            render_data=render_data,
1386            instances=instances,
1387            objectives=objeectives,
1388            perks=perks,
1389            plug_states=plug_states,
1390            reusable_plugs=reusable_plugs,
1391            plug_objectives=plug_objectives,
1392        )

Deserialize a JSON objects within the itemComponents key.`

def deserialize_character_component( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.CharacterComponent:
1394    def deserialize_character_component(  # type: ignore[call-arg]
1395        self, payload: typedefs.JSONObject
1396    ) -> components.CharacterComponent:
1397
1398        character_: typing.Optional[character.Character] = None
1399        if raw_singuler_character := payload.get("character"):
1400            character_ = self.deserialize_character(raw_singuler_character["data"])
1401
1402        inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1403        if raw_inventory := payload.get("inventory"):
1404            if "data" in raw_inventory:
1405                inventory = self.deserialize_profile_items(raw_inventory["data"])
1406
1407        activities: typing.Optional[activity.CharacterActivity] = None
1408        if raw_activities := payload.get("activities"):
1409            activities = self.deserialize_character_activity(raw_activities["data"])
1410
1411        equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1412        if raw_equipments := payload.get("equipment"):
1413            equipment = self.deserialize_profile_items(raw_equipments["data"])
1414
1415        progressions_: typing.Optional[character.CharacterProgression] = None
1416        if raw_progressions := payload.get("progressions"):
1417            progressions_ = self.deserialize_character_progressions(
1418                raw_progressions["data"]
1419            )
1420
1421        render_data: typing.Optional[character.RenderedData] = None
1422        if raw_render_data := payload.get("renderData"):
1423            render_data = self.deserialize_character_render_data(
1424                raw_render_data["data"]
1425            )
1426
1427        character_records: typing.Optional[
1428            collections.Mapping[int, records.CharacterRecord]
1429        ] = None
1430        if raw_char_records := payload.get("records"):
1431            character_records = self.deserialize_characters_records(
1432                raw_char_records["data"]
1433            )
1434
1435        item_components: typing.Optional[components.ItemsComponent] = None
1436        if raw_item_components := payload.get("itemComponents"):
1437            item_components = self.deserialize_items_component(raw_item_components)
1438
1439        nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1440        if raw_nodes := payload.get("presentationNodes"):
1441            nodes = {
1442                int(node_hash): self._deserialize_node(node)
1443                for node_hash, node in raw_nodes["data"]["nodes"].items()
1444            }
1445
1446        collectibles: typing.Optional[items.Collectible] = None
1447        if raw_collectibles := payload.get("collectibles"):
1448            collectibles = self._deserialize_collectible(raw_collectibles["data"])
1449
1450        currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None
1451        if raw_currencies := payload.get("currencyLookups"):
1452            if "data" in raw_currencies:
1453                currency_lookups = self._deserialize_currencies(raw_currencies)
1454
1455        return components.CharacterComponent(
1456            activities=activities,
1457            equipment=equipment,
1458            inventory=inventory,
1459            progressions=progressions_,
1460            render_data=render_data,
1461            character=character_,
1462            character_records=character_records,
1463            profile_records=None,
1464            item_components=item_components,
1465            currency_lookups=currency_lookups,
1466            collectibles=collectibles,
1467            nodes=nodes,
1468        )

Deserialize a JSON payload of Destiny 2 character component.

Parameters
Returns
def deserialize_inventory_results( self, payload: dict[str, typing.Any]) -> aiobungie.FlatIterator[aiobungie.crates.entity.SearchableEntity]:
1496    def deserialize_inventory_results(
1497        self, payload: typedefs.JSONObject
1498    ) -> iterators.FlatIterator[entity.SearchableEntity]:
1499        suggested_words: list[str] = payload["suggestedWords"]
1500
1501        def _check_unknown(s: str) -> undefined.UndefinedOr[str]:
1502            return s if not typedefs.is_unknown(s) else undefined.Undefined
1503
1504        return iterators.FlatIterator(
1505            [
1506                entity.SearchableEntity(
1507                    net=self._net,
1508                    hash=data["hash"],
1509                    entity_type=data["entityType"],
1510                    weight=data["weight"],
1511                    suggested_words=suggested_words,
1512                    name=data["displayProperties"]["name"],
1513                    has_icon=data["displayProperties"]["hasIcon"],
1514                    description=_check_unknown(
1515                        data["displayProperties"]["description"]
1516                    ),
1517                    icon=assets.Image(data["displayProperties"]["icon"]),
1518                )
1519                for data in payload["results"]["results"]
1520            ]
1521        )

Deserialize results of searched Destiny2 entities.

Parameters
Returns
def deserialize_inventory_entity( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.entity.InventoryEntity:
1550    def deserialize_inventory_entity(  # noqa: C901 Too complex.
1551        self, payload: typedefs.JSONObject, /
1552    ) -> entity.InventoryEntity:
1553
1554        props = self._set_entity_attrs(payload)
1555        objects = self._deserialize_inventory_item_objects(payload)
1556
1557        collectible_hash: typing.Optional[int] = None
1558        if raw_collectible_hash := payload.get("collectibleHash"):
1559            collectible_hash = int(raw_collectible_hash)
1560
1561        secondary_icon: undefined.UndefinedOr[assets.Image] = undefined.Undefined
1562        if raw_second_icon := payload.get("secondaryIcon"):
1563            secondary_icon = assets.Image(raw_second_icon)
1564
1565        secondary_overlay: undefined.UndefinedOr[assets.Image] = undefined.Undefined
1566        if raw_second_overlay := payload.get("secondaryOverlay"):
1567            secondary_overlay = assets.Image(raw_second_overlay)
1568
1569        secondary_special: undefined.UndefinedOr[assets.Image] = undefined.Undefined
1570        if raw_second_special := payload.get("secondarySpecial"):
1571            secondary_special = assets.Image(raw_second_special)
1572
1573        screenshot: undefined.UndefinedOr[assets.Image] = undefined.Undefined
1574        if raw_screenshot := payload.get("screenshot"):
1575            screenshot = assets.Image(raw_screenshot)
1576
1577        watermark_icon: typing.Optional[assets.Image] = None
1578        if raw_watermark_icon := payload.get("iconWatermark"):
1579            watermark_icon = assets.Image(raw_watermark_icon)
1580
1581        watermark_shelved: typing.Optional[assets.Image] = None
1582        if raw_watermark_shelved := payload.get("iconWatermarkShelved"):
1583            watermark_shelved = assets.Image(raw_watermark_shelved)
1584
1585        about: undefined.UndefinedOr[str] = undefined.Undefined
1586        if (raw_about := payload.get("flavorText")) and not typedefs.is_unknown(
1587            raw_about
1588        ):
1589            about = raw_about
1590
1591        ui_item_style: undefined.UndefinedOr[str] = undefined.Undefined
1592        if (
1593            raw_ui_style := payload.get("uiItemDisplayStyle")
1594        ) and not typedefs.is_unknown(raw_ui_style):
1595            ui_item_style = raw_ui_style
1596
1597        tier_and_name: undefined.UndefinedOr[str] = undefined.Undefined
1598        if (
1599            raw_tier_and_name := payload.get("itemTypeAndTierDisplayName")
1600        ) and not typedefs.is_unknown(raw_tier_and_name):
1601            tier_and_name = raw_tier_and_name
1602
1603        type_name: undefined.UndefinedOr[str] = undefined.Undefined
1604        if (
1605            raw_type_name := payload.get("itemTypeDisplayName")
1606        ) and not typedefs.is_unknown(raw_type_name):
1607            type_name = raw_type_name
1608
1609        display_source: undefined.UndefinedOr[str] = undefined.Undefined
1610        if (
1611            raw_display_source := payload.get("displaySource")
1612        ) and not typedefs.is_unknown(raw_display_source):
1613            display_source = raw_display_source
1614
1615        lorehash: typing.Optional[int] = None
1616        if raw_lore_hash := payload.get("loreHash"):
1617            lorehash = int(raw_lore_hash)
1618
1619        summary_hash: typing.Optional[int] = None
1620        if raw_summary_hash := payload.get("summaryItemHash"):
1621            summary_hash = raw_summary_hash
1622
1623        breaker_type_hash: typing.Optional[int] = None
1624        if raw_breaker_type_hash := payload.get("breakerTypeHash"):
1625            breaker_type_hash = int(raw_breaker_type_hash)
1626
1627        damage_types: typing.Optional[collections.Sequence[int]] = None
1628        if raw_damage_types := payload.get("damageTypes"):
1629            damage_types = [int(type_) for type_ in raw_damage_types]
1630
1631        damagetype_hashes: typing.Optional[collections.Sequence[int]] = None
1632        if raw_damagetype_hashes := payload.get("damageTypeHashes"):
1633            damagetype_hashes = [int(type_) for type_ in raw_damagetype_hashes]
1634
1635        default_damagetype_hash: typing.Optional[int] = None
1636        if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"):
1637            default_damagetype_hash = int(raw_defaultdmg_hash)
1638
1639        emblem_objective_hash: typing.Optional[int] = None
1640        if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"):
1641            emblem_objective_hash = int(raw_emblem_obj_hash)
1642
1643        tier_type: typing.Optional[enums.TierType] = None
1644        tier: typing.Optional[enums.ItemTier] = None
1645        bucket_hash: typing.Optional[int] = None
1646        recovery_hash: typing.Optional[int] = None
1647        tier_name: undefined.UndefinedOr[str] = undefined.Undefined
1648        isinstance_item: bool = False
1649        expire_tool_tip: undefined.UndefinedOr[str] = undefined.Undefined
1650        expire_in_orbit_message: undefined.UndefinedOr[str] = undefined.Undefined
1651        suppress_expiration: bool = False
1652        max_stack_size: typing.Optional[int] = None
1653        stack_label: undefined.UndefinedOr[str] = undefined.Undefined
1654
1655        if inventory := payload.get("inventory"):
1656            tier_type = enums.TierType(int(inventory["tierType"]))
1657            tier = enums.ItemTier(int(inventory["tierTypeHash"]))
1658            bucket_hash = int(inventory["bucketTypeHash"])
1659            recovery_hash = int(inventory["recoveryBucketTypeHash"])
1660            tier_name = inventory["tierTypeName"]
1661            isinstance_item = inventory["isInstanceItem"]
1662            suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"]
1663            max_stack_size = int(inventory["maxStackSize"])
1664
1665            try:
1666                stack_label = inventory["stackUniqueLabel"]
1667            except KeyError:
1668                pass
1669
1670        return entity.InventoryEntity(
1671            net=self._net,
1672            collectible_hash=collectible_hash,
1673            name=props.name,
1674            about=about,
1675            emblem_objective_hash=emblem_objective_hash,
1676            suppress_expiration=suppress_expiration,
1677            max_stack_size=max_stack_size,
1678            stack_label=stack_label,
1679            tier=tier,
1680            tier_type=tier_type,
1681            tier_name=tier_name,
1682            bucket_hash=bucket_hash,
1683            recovery_bucket_hash=recovery_hash,
1684            isinstance_item=isinstance_item,
1685            expire_in_orbit_message=expire_in_orbit_message,
1686            expiration_tooltip=expire_tool_tip,
1687            lore_hash=lorehash,
1688            type_and_tier_name=tier_and_name,
1689            summary_hash=summary_hash,
1690            ui_display_style=ui_item_style,
1691            type_name=type_name,
1692            breaker_type_hash=breaker_type_hash,
1693            description=props.description,
1694            display_source=display_source,
1695            hash=props.hash,
1696            damage_types=damage_types,
1697            index=props.index,
1698            icon=props.icon,
1699            has_icon=props.has_icon,
1700            screenshot=screenshot,
1701            watermark_icon=watermark_icon,
1702            watermark_shelved=watermark_shelved,
1703            secondary_icon=secondary_icon,
1704            secondary_overlay=secondary_overlay,
1705            secondary_special=secondary_special,
1706            type=enums.ItemType(int(payload["itemType"])),
1707            trait_hashes=[int(id_) for id_ in payload.get("traitHashes", [])],
1708            trait_ids=[trait for trait in payload.get("traitIds", [])],
1709            category_hashes=[int(hash_) for hash_ in payload["itemCategoryHashes"]],
1710            item_class=enums.Class(int(payload["classType"])),
1711            sub_type=enums.ItemSubType(int(payload["itemSubType"])),
1712            breaker_type=int(payload["breakerType"]),
1713            default_damagetype=int(payload["defaultDamageType"]),
1714            default_damagetype_hash=default_damagetype_hash,
1715            damagetype_hashes=damagetype_hashes,
1716            tooltip_notifications=payload["tooltipNotifications"],
1717            not_transferable=payload["nonTransferrable"],
1718            allow_actions=payload["allowActions"],
1719            is_equippable=payload["equippable"],
1720            objects=objects,
1721            background_colors=payload.get("backgroundColor", {}),
1722            season_hash=payload.get("seasonHash"),
1723            has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"],
1724        )

Deserialize a JSON payload of an inventory entity item information.

This can be any item from DestinyInventoryItemDefinition definition.

Parameters
Returns
def deserialize_objective_entity( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.entity.ObjectiveEntity:
1726    def deserialize_objective_entity(
1727        self, payload: typedefs.JSONObject, /
1728    ) -> entity.ObjectiveEntity:
1729        props = self._set_entity_attrs(payload)
1730        return entity.ObjectiveEntity(
1731            net=self._net,
1732            hash=props.hash,
1733            index=props.index,
1734            description=props.description,
1735            name=props.name,
1736            has_icon=props.has_icon,
1737            icon=props.icon,
1738            unlock_value_hash=payload["unlockValueHash"],
1739            completion_value=payload["completionValue"],
1740            scope=entity.GatingScope(int(payload["scope"])),
1741            location_hash=payload["locationHash"],
1742            allowed_negative_value=payload["allowNegativeValue"],
1743            allowed_value_change=payload["allowValueChangeWhenCompleted"],
1744            counting_downward=payload["isCountingDownward"],
1745            value_style=entity.ValueUIStyle(int(payload["valueStyle"])),
1746            progress_description=payload["progressDescription"],
1747            perks=payload["perks"],
1748            stats=payload["stats"],
1749            minimum_visibility=payload["minimumVisibilityThreshold"],
1750            allow_over_completion=payload["allowOvercompletion"],
1751            show_value_style=payload["showValueOnComplete"],
1752            display_only_objective=payload["isDisplayOnlyObjective"],
1753            complete_value_style=entity.ValueUIStyle(
1754                int(payload["completedValueStyle"])
1755            ),
1756            progress_value_style=entity.ValueUIStyle(
1757                int(payload["inProgressValueStyle"])
1758            ),
1759            ui_label=payload["uiLabel"],
1760            ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])),
1761        )

Deserialize a JSON payload of an objective entity information.

Parameters
Returns
def deserialize_activity( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.activity.Activity:
1789    def deserialize_activity(
1790        self,
1791        payload: typedefs.JSONObject,
1792        /,
1793    ) -> activity.Activity:
1794        period = time.clean_date(payload["period"])
1795        details = payload["activityDetails"]
1796        ref_id = int(details["referenceId"])
1797        instance_id = int(details["instanceId"])
1798        mode = enums.GameMode(details["mode"])
1799        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1800        is_private = details["isPrivate"]
1801        membership_type = enums.MembershipType(int(details["membershipType"]))
1802
1803        # Since we're using the same fields for post activity method
1804        # this check is required since post activity doesn't values values
1805        values = self._deserialize_activity_values(payload["values"])
1806
1807        return activity.Activity(
1808            net=self._net,
1809            hash=ref_id,
1810            instance_id=instance_id,
1811            mode=mode,
1812            modes=modes,
1813            is_private=is_private,
1814            membership_type=membership_type,
1815            occurred_at=period,
1816            values=values,
1817        )

Deserialize a JSON payload of an activity history information.

Parameters
Returns
def deserialize_activities( self, payload: dict[str, typing.Any]) -> aiobungie.FlatIterator[aiobungie.crates.activity.Activity]:
1819    def deserialize_activities(
1820        self, payload: typedefs.JSONObject
1821    ) -> iterators.FlatIterator[activity.Activity]:
1822        return iterators.FlatIterator(
1823            [
1824                self.deserialize_activity(activity_)
1825                for activity_ in payload["activities"]
1826            ]
1827        )

Deserialize a JSON payload of an array of activity history information.

Parameters
Returns
  • aiobungie.iterators.FlatIterator[aiobungie.crates.Activity]: Am iterator over activity objects of the deserialized payload.
def deserialize_extended_weapon_values( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.ExtendedWeaponValues:
1829    def deserialize_extended_weapon_values(
1830        self, payload: typedefs.JSONObject
1831    ) -> activity.ExtendedWeaponValues:
1832
1833        assists: typing.Optional[int] = None
1834        if raw_assists := payload["values"].get("uniqueWeaponAssists"):
1835            assists = raw_assists["basic"]["value"]
1836        assists_damage: typing.Optional[int] = None
1837
1838        if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"):
1839            assists_damage = raw_assists_damage["basic"]["value"]
1840
1841        return activity.ExtendedWeaponValues(
1842            reference_id=int(payload["referenceId"]),
1843            kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"],
1844            precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][
1845                "value"
1846            ],
1847            assists=assists,
1848            assists_damage=assists_damage,
1849            precision_kills_percentage=(
1850                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"],
1851                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][
1852                    "displayValue"
1853                ],
1854            ),
1855        )

Deserialize values of extended weapons JSON object.

Parameters
Returns
def deserialize_post_activity_player( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.activity.PostActivityPlayer:
1878    def deserialize_post_activity_player(
1879        self, payload: typedefs.JSONObject, /
1880    ) -> activity.PostActivityPlayer:
1881        player = payload["player"]
1882
1883        class_hash: typedefs.NoneOr[int] = None
1884        if (class_hash := player.get("classHash")) is not None:
1885            class_hash = class_hash
1886
1887        race_hash: typedefs.NoneOr[int] = None
1888        if (race_hash := player.get("raceHash")) is not None:
1889            race_hash = race_hash
1890
1891        gender_hash: typedefs.NoneOr[int] = None
1892        if (gender_hash := player.get("genderHash")) is not None:
1893            gender_hash = gender_hash
1894
1895        character_class: undefined.UndefinedOr[str] = undefined.Undefined
1896        if (
1897            character_class := player.get("characterClass")
1898        ) and not typedefs.is_unknown(character_class):
1899            character_class = character_class
1900
1901        character_level: typedefs.NoneOr[int] = None
1902        if (character_level := player.get("characterLevel")) is not None:
1903            character_level = character_level
1904
1905        return activity.PostActivityPlayer(
1906            standing=int(payload["standing"]),
1907            score=int(payload["score"]["basic"]["value"]),
1908            character_id=payload["characterId"],
1909            destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]),
1910            character_class=character_class,
1911            character_level=character_level,
1912            race_hash=race_hash,
1913            gender_hash=gender_hash,
1914            class_hash=class_hash,
1915            light_level=int(player["lightLevel"]),
1916            emblem_hash=int(player["emblemHash"]),
1917            values=self._deserialize_activity_values(payload["values"]),
1918            extended_values=self._deserialize_extended_values(payload["extended"]),
1919        )

Deserialize a JSON payload of a post activity player information.

Parameters
Returns
def deserialize_post_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.PostActivity:
1931    def deserialize_post_activity(
1932        self, payload: typedefs.JSONObject
1933    ) -> activity.PostActivity:
1934        period = time.clean_date(payload["period"])
1935        details = payload["activityDetails"]
1936        ref_id = int(details["referenceId"])
1937        instance_id = int(details["instanceId"])
1938        mode = enums.GameMode(details["mode"])
1939        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1940        is_private = details["isPrivate"]
1941        membership_type = enums.MembershipType(int(details["membershipType"]))
1942        return activity.PostActivity(
1943            net=self._net,
1944            hash=ref_id,
1945            membership_type=membership_type,
1946            instance_id=instance_id,
1947            mode=mode,
1948            modes=modes,
1949            is_private=is_private,
1950            occurred_at=period,
1951            starting_phase=int(payload["startingPhaseIndex"]),
1952            players=[
1953                self.deserialize_post_activity_player(player)
1954                for player in payload["entries"]
1955            ],
1956            teams=[
1957                self._deserialize_post_activity_team(team) for team in payload["teams"]
1958            ],
1959        )

Deserialize a JSON payload of a post activity information.

Parameters
Returns
def deserialize_aggregated_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.AggregatedActivity:
1997    def deserialize_aggregated_activity(
1998        self, payload: typedefs.JSONObject
1999    ) -> activity.AggregatedActivity:
2000        return activity.AggregatedActivity(
2001            hash=int(payload["activityHash"]),
2002            values=self._deserialize_aggregated_activity_values(payload["values"]),
2003        )

Deserialize a JSON payload of an aggregated activity.

Parameters
Returns
def deserialize_aggregated_activities( self, payload: dict[str, typing.Any]) -> aiobungie.FlatIterator[aiobungie.crates.activity.AggregatedActivity]:
2005    def deserialize_aggregated_activities(
2006        self, payload: typedefs.JSONObject
2007    ) -> iterators.FlatIterator[activity.AggregatedActivity]:
2008        return iterators.FlatIterator(
2009            [
2010                self.deserialize_aggregated_activity(activity)
2011                for activity in payload["activities"]
2012            ]
2013        )

Deserialize a JSON payload of an array of aggregated activities.

Parameters
Returns
def deserialize_linked_profiles( self, payload: dict[str, typing.Any]) -> aiobungie.crates.profile.LinkedProfile:
2015    def deserialize_linked_profiles(
2016        self, payload: typedefs.JSONObject
2017    ) -> profile.LinkedProfile:
2018        bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"])
2019        error_profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2020        profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2021
2022        if raw_profile := payload.get("profiles"):
2023            for pfile in raw_profile:
2024                profiles_vec.append(self.deserialize_destiny_membership(pfile))
2025
2026        if raw_profiles_with_errors := payload.get("profilesWithErrors"):
2027            for raw_error_pfile in raw_profiles_with_errors:
2028                if error_pfile := raw_error_pfile.get("infoCard"):
2029                    error_profiles_vec.append(
2030                        self.deserialize_destiny_membership(error_pfile)
2031                    )
2032
2033        return profile.LinkedProfile(
2034            net=self._net,
2035            bungie=bungie_user,
2036            profiles=profiles_vec,
2037            profiles_with_errors=error_profiles_vec,
2038        )

Deserialize a JSON payload of Bungie.net hard linked profile information.

Parameters
Returns
def deserialize_clan_banners( self, payload: dict[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.clans.ClanBanner]:
2040    def deserialize_clan_banners(
2041        self, payload: typedefs.JSONObject
2042    ) -> collections.Sequence[clans.ClanBanner]:
2043        banners_seq: typing.MutableSequence[clans.ClanBanner] = []
2044        if banners := payload.get("clanBannerDecals"):
2045            for k, v in banners.items():
2046                banner_obj = clans.ClanBanner(
2047                    id=int(k),
2048                    foreground=assets.Image(v["foregroundPath"]),
2049                    background=assets.Image(v["backgroundPath"]),
2050                )
2051                banners_seq.append(banner_obj)
2052        return banners_seq

Deserialize a JSON array of a clan banners information.

Parameters
Returns
def deserialize_public_milestone_content( self, payload: dict[str, typing.Any]) -> aiobungie.crates.milestones.MilestoneContent:
2054    def deserialize_public_milestone_content(
2055        self, payload: typedefs.JSONObject
2056    ) -> milestones.MilestoneContent:
2057        items_categoris: typedefs.NoneOr[milestones.MilestoneItems] = None
2058        if raw_categories := payload.get("itemCategories"):
2059            for item in raw_categories:
2060                title = undefined.Undefined
2061                if raw_title := item.get("title"):
2062                    if raw_title != typedefs.Unknown:
2063                        title = raw_title
2064                if raw_hashes := item.get("itemHashes"):
2065                    hashes: collections.Sequence[int] = raw_hashes
2066
2067                items_categoris = milestones.MilestoneItems(title=title, hashes=hashes)
2068
2069        about = undefined.Undefined
2070        if (raw_about := payload["about"]) != typedefs.Unknown:
2071            about = raw_about
2072
2073        status = undefined.Undefined
2074        if (raw_status := payload["status"]) != typedefs.Unknown:
2075            status = raw_status
2076
2077        tips: typing.MutableSequence[undefined.UndefinedOr[str]] = []
2078        if raw_tips := payload.get("tips"):
2079            for raw_tip in raw_tips:
2080                if raw_tip == typedefs.Unknown:
2081                    raw_tip = undefined.Undefined
2082                tips.append(raw_tip)
2083
2084        return milestones.MilestoneContent(
2085            about=about, status=status, tips=tips, items=items_categoris
2086        )

Deserialize a JSON payload of milestone content information.

Parameters
Returns
def deserialize_friend( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.friends.Friend:
2088    def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend:
2089        name = undefined.Undefined
2090        if (raw_name := payload["bungieGlobalDisplayName"]) != typedefs.Unknown:
2091            name = raw_name
2092
2093        bungie_user: typedefs.NoneOr[user.BungieUser] = None
2094
2095        if raw_bungie_user := payload.get("bungieNetUser"):
2096            bungie_user = self.deserialize_bungie_user(raw_bungie_user)
2097
2098        return friends.Friend(
2099            net=self._net,
2100            id=int(payload["lastSeenAsMembershipId"]),
2101            name=name,
2102            code=payload.get("bungieGlobalDisplayNameCode"),
2103            relationship=enums.Relationship(payload["relationship"]),
2104            user=bungie_user,
2105            online_status=enums.Presence(payload["onlineStatus"]),
2106            online_title=payload["onlineTitle"],
2107            type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]),
2108        )

Deserialize a JSON payload of a Bungie friend information.

Parameters
Returns
def deserialize_friends( self, payload: dict[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.friends.Friend]:
2110    def deserialize_friends(
2111        self, payload: typedefs.JSONObject
2112    ) -> collections.Sequence[friends.Friend]:
2113        mut_seq: typing.MutableSequence[friends.Friend] = []
2114        if raw_friends := payload.get("friends"):
2115            for friend in raw_friends:
2116                mut_seq.append(self.deserialize_friend(friend))
2117        return mut_seq

Deserialize a JSON sequence of Bungie friends information.

This is usually used to deserialize the incoming/outgoing friend requests.

Parameters
Returns
def deserialize_friend_requests( self, payload: dict[str, typing.Any]) -> aiobungie.crates.friends.FriendRequestView:
2119    def deserialize_friend_requests(
2120        self, payload: typedefs.JSONObject
2121    ) -> friends.FriendRequestView:
2122        incoming: typing.MutableSequence[friends.Friend] = []
2123        outgoing: typing.MutableSequence[friends.Friend] = []
2124
2125        if raw_incoming_requests := payload.get("incomingRequests"):
2126            for incoming_request in raw_incoming_requests:
2127                incoming.append(self.deserialize_friend(incoming_request))
2128
2129        if raw_outgoing_requests := payload.get("outgoingRequests"):
2130            for outgoing_request in raw_outgoing_requests:
2131                outgoing.append(self.deserialize_friend(outgoing_request))
2132
2133        return friends.FriendRequestView(incoming=incoming, outgoing=outgoing)

Deserialize a JSON sequence of Bungie friend requests information.

This is used for incoming/outgoing friend requests.

Parameters
Returns
def deserialize_fireteams( self, payload: dict[str, typing.Any]) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]]:
2158    def deserialize_fireteams(
2159        self, payload: typedefs.JSONObject
2160    ) -> typedefs.NoneOr[collections.Sequence[fireteams.Fireteam]]:
2161        fireteams_: typing.MutableSequence[fireteams.Fireteam] = []
2162
2163        result: list[typedefs.JSONObject]
2164        if not (result := payload["results"]):
2165            return None
2166        for elem in result:
2167            fireteams_.append(
2168                self._set_fireteam_fields(
2169                    elem, total_results=int(payload["totalResults"])
2170                )
2171            )
2172        return fireteams_

Deserialize a JSON sequence of Bungie fireteams information.

Parameters
Returns
def deserialize_fireteam_destiny_users( self, payload: dict[str, typing.Any]) -> aiobungie.crates.fireteams.FireteamUser:
2174    def deserialize_fireteam_destiny_users(
2175        self, payload: typedefs.JSONObject
2176    ) -> fireteams.FireteamUser:
2177        destiny_obj = self.deserialize_destiny_membership(payload)
2178        # We could helpers.just return a DestinyMembership object but this is
2179        # missing the fireteam display name and id fields.
2180        return fireteams.FireteamUser(
2181            net=self._net,
2182            id=destiny_obj.id,
2183            code=destiny_obj.code,
2184            icon=destiny_obj.icon,
2185            types=destiny_obj.types,
2186            type=destiny_obj.type,
2187            is_public=destiny_obj.is_public,
2188            crossave_override=destiny_obj.crossave_override,
2189            name=destiny_obj.name,
2190            last_seen_name=destiny_obj.last_seen_name,
2191            fireteam_display_name=payload["FireteamDisplayName"],
2192            fireteam_membership_id=enums.MembershipType(
2193                payload["FireteamMembershipType"]
2194            ),
2195        )

Deserialize a JSON payload of Bungie fireteam destiny users information.

Parameters
Returns
def deserialize_fireteam_members( self, payload: dict[str, typing.Any], *, alternatives: bool = False) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.FireteamMember]]:
2197    def deserialize_fireteam_members(
2198        self, payload: typedefs.JSONObject, *, alternatives: bool = False
2199    ) -> typing.Optional[collections.Sequence[fireteams.FireteamMember]]:
2200        members_: list[fireteams.FireteamMember] = []
2201        if members := payload.get("Members" if not alternatives else "Alternates"):
2202            for member in members:
2203                bungie_fields = self.deserialize_partial_bungie_user(member)
2204                members_fields = fireteams.FireteamMember(
2205                    destiny_user=self.deserialize_fireteam_destiny_users(member),
2206                    has_microphone=member["hasMicrophone"],
2207                    character_id=int(member["characterId"]),
2208                    date_joined=time.clean_date(member["dateJoined"]),
2209                    last_platform_invite_date=time.clean_date(
2210                        member["lastPlatformInviteAttemptDate"]
2211                    ),
2212                    last_platform_invite_result=int(
2213                        member["lastPlatformInviteAttemptResult"]
2214                    ),
2215                    net=self._net,
2216                    name=bungie_fields.name,
2217                    id=bungie_fields.id,
2218                    icon=bungie_fields.icon,
2219                    is_public=bungie_fields.is_public,
2220                    crossave_override=bungie_fields.crossave_override,
2221                    types=bungie_fields.types,
2222                    type=bungie_fields.type,
2223                )
2224                members_.append(members_fields)
2225        else:
2226            return None
2227        return members_

Deserialize a JSON sequence of Bungie fireteam members information.

Parameters
  • payload (aiobungie.typedefs.JSONObject): The JSON payload.
  • alternatives (bool): If set to True, Then it will deserialize the alternatives data in the payload. If not the it will just deserialize the members data.
Returns
def deserialize_available_fireteams( self, data: dict[str, typing.Any], *, no_results: bool = False) -> Union[aiobungie.crates.fireteams.AvailableFireteam, collections.abc.Sequence[aiobungie.crates.fireteams.AvailableFireteam]]:
2229    def deserialize_available_fireteams(
2230        self,
2231        data: typedefs.JSONObject,
2232        *,
2233        no_results: bool = False,
2234    ) -> typing.Union[
2235        fireteams.AvailableFireteam, collections.Sequence[fireteams.AvailableFireteam]
2236    ]:
2237        fireteams_: list[fireteams.AvailableFireteam] = []
2238
2239        # This needs to be used outside the results
2240        # JSON key.
2241        if no_results is True:
2242            payload = data
2243
2244        if result := payload.get("results"):
2245
2246            for fireteam in result:
2247                found_fireteams = self._set_fireteam_fields(fireteam["Summary"])
2248                fireteams_fields = fireteams.AvailableFireteam(
2249                    id=found_fireteams.id,
2250                    group_id=found_fireteams.group_id,
2251                    platform=found_fireteams.platform,
2252                    activity_type=found_fireteams.activity_type,
2253                    is_immediate=found_fireteams.is_immediate,
2254                    is_public=found_fireteams.is_public,
2255                    is_valid=found_fireteams.is_valid,
2256                    owner_id=found_fireteams.owner_id,
2257                    player_slot_count=found_fireteams.player_slot_count,
2258                    available_player_slots=found_fireteams.available_player_slots,
2259                    available_alternate_slots=found_fireteams.available_alternate_slots,
2260                    title=found_fireteams.title,
2261                    date_created=found_fireteams.date_created,
2262                    locale=found_fireteams.locale,
2263                    last_modified=found_fireteams.last_modified,
2264                    total_results=found_fireteams.total_results,
2265                    members=self.deserialize_fireteam_members(payload),
2266                    alternatives=self.deserialize_fireteam_members(
2267                        payload, alternatives=True
2268                    ),
2269                )
2270            fireteams_.append(fireteams_fields)
2271            if no_results:
2272                return fireteams_fields
2273        return fireteams_

Deserialize a JSON payload of a sequence of/fireteam information.

Parameters
  • payload (aiobungie.typedefs.JSONObject): The JSON payload.
  • no_results (bool): Whether to deserialize the data from results in the payload or not.
Returns
  • typing.Union[aiobungie.crates.fireteams.AvailableFireteam, collections.Sequence[aiobungie.crates.fireteams.AvailableFireteam]] # noqa (E501): An available fireteam or a sequence of available fireteam.
def deserialize_fireteam_party( self, payload: dict[str, typing.Any]) -> aiobungie.crates.fireteams.FireteamParty:
2275    def deserialize_fireteam_party(
2276        self, payload: typedefs.JSONObject
2277    ) -> fireteams.FireteamParty:
2278        last_destination_hash: typing.Optional[int] = None
2279        if raw_dest_hash := payload.get("lastOrbitedDestinationHash"):
2280            last_destination_hash = int(raw_dest_hash)
2281
2282        return fireteams.FireteamParty(
2283            members=[
2284                self._deserialize_fireteam_party_member(member)
2285                for member in payload["partyMembers"]
2286            ],
2287            activity=self._deserialize_fireteam_party_current_activity(
2288                payload["currentActivity"]
2289            ),
2290            settings=self._deserialize_fireteam_party_settings(payload["joinability"]),
2291            last_destination_hash=last_destination_hash,
2292            tracking=payload["tracking"],
2293        )

Deserialize a JSON payload of profileTransitory component response.

Parameters
Returns
def deserialize_seasonal_artifact(self, payload: dict[str, typing.Any]) -> aiobungie.crates.season.Artifact:
2340    def deserialize_seasonal_artifact(
2341        self, payload: typedefs.JSONObject
2342    ) -> season.Artifact:
2343        if raw_artifact := payload.get("seasonalArtifact"):
2344            if points := raw_artifact.get("pointProgression"):
2345                points_prog = progressions.Progression(
2346                    hash=points["progressionHash"],
2347                    level=points["level"],
2348                    cap=points["levelCap"],
2349                    daily_limit=points["dailyLimit"],
2350                    weekly_limit=points["weeklyLimit"],
2351                    current_progress=points["currentProgress"],
2352                    daily_progress=points["dailyProgress"],
2353                    needed=points["progressToNextLevel"],
2354                    next_level=points["nextLevelAt"],
2355                )
2356
2357            if bonus := raw_artifact.get("powerBonusProgression"):
2358                power_bonus_prog = progressions.Progression(
2359                    hash=bonus["progressionHash"],
2360                    level=bonus["level"],
2361                    cap=bonus["levelCap"],
2362                    daily_limit=bonus["dailyLimit"],
2363                    weekly_limit=bonus["weeklyLimit"],
2364                    current_progress=bonus["currentProgress"],
2365                    daily_progress=bonus["dailyProgress"],
2366                    needed=bonus["progressToNextLevel"],
2367                    next_level=bonus["nextLevelAt"],
2368                )
2369            artifact = season.Artifact(
2370                net=self._net,
2371                hash=raw_artifact["artifactHash"],
2372                power_bonus=raw_artifact["powerBonus"],
2373                acquired_points=raw_artifact["pointsAcquired"],
2374                bonus=power_bonus_prog,
2375                points=points_prog,
2376            )
2377        return artifact

Deserialize a JSON payload of a Destiny 2 seasonal artifact information.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_profile_progression( self, payload: dict[str, typing.Any]) -> aiobungie.crates.profile.ProfileProgression:
2379    def deserialize_profile_progression(
2380        self, payload: typedefs.JSONObject
2381    ) -> profile.ProfileProgression:
2382        return profile.ProfileProgression(
2383            artifact=self.deserialize_seasonal_artifact(payload["data"]),
2384            checklist={
2385                int(check_id): checklists
2386                for check_id, checklists in payload["data"]["checklists"].items()
2387            },
2388        )

Deserialize a JSON payload of a profile progression component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_instanced_item( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemInstance:
2390    def deserialize_instanced_item(
2391        self, payload: typedefs.JSONObject
2392    ) -> items.ItemInstance:
2393        damage_type_hash: typing.Optional[int] = None
2394        if raw_damagetype_hash := payload.get("damageTypeHash"):
2395            damage_type_hash = int(raw_damagetype_hash)
2396
2397        required_hashes: typing.Optional[collections.Collection[int]] = None
2398        if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"):
2399            required_hashes = [int(raw_hash) for raw_hash in raw_required_hashes]
2400
2401        breaker_type: typing.Optional[items.ItemBreakerType] = None
2402        if raw_break_type := payload.get("breakerType"):
2403            breaker_type = items.ItemBreakerType(int(raw_break_type))
2404
2405        breaker_type_hash: typing.Optional[int] = None
2406        if raw_break_type_hash := payload.get("breakerTypeHash"):
2407            breaker_type_hash = int(raw_break_type_hash)
2408
2409        energy: typing.Optional[items.ItemEnergy] = None
2410        if raw_energy := payload.get("energy"):
2411            energy = self.deserialize_item_energy(raw_energy)
2412
2413        primary_stats = None
2414        if raw_primary_stats := payload.get("primaryStat"):
2415            primary_stats = self.deserialize_item_stats_view(raw_primary_stats)
2416
2417        return items.ItemInstance(
2418            damage_type=enums.DamageType(int(payload["damageType"])),
2419            damage_type_hash=damage_type_hash,
2420            primary_stat=primary_stats,
2421            item_level=int(payload["itemLevel"]),
2422            quality=int(payload["quality"]),
2423            is_equipped=payload["isEquipped"],
2424            can_equip=payload["canEquip"],
2425            equip_required_level=int(payload["equipRequiredLevel"]),
2426            required_equip_unlock_hashes=required_hashes,
2427            cant_equip_reason=int(payload["cannotEquipReason"]),
2428            breaker_type=breaker_type,
2429            breaker_type_hash=breaker_type_hash,
2430            energy=energy,
2431        )

Deserialize a JSON object into an instanced item.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_item_energy( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemEnergy:
2433    def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy:
2434        energy_hash: typing.Optional[int] = None
2435        if raw_energy_hash := payload.get("energyTypeHash"):
2436            energy_hash = int(raw_energy_hash)
2437
2438        return items.ItemEnergy(
2439            hash=energy_hash,
2440            type=items.ItemEnergyType(int(payload["energyType"])),
2441            capacity=int(payload["energyCapacity"]),
2442            used_energy=int(payload["energyUsed"]),
2443            unused_energy=int(payload["energyUnused"]),
2444        )
def deserialize_item_perk(self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemPerk:
2446    def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk:
2447        perk_hash: typing.Optional[int] = None
2448        if raw_perk_hash := payload.get("perkHash"):
2449            perk_hash = int(raw_perk_hash)
2450
2451        return items.ItemPerk(
2452            hash=perk_hash,
2453            icon=assets.Image(payload["iconPath"]),
2454            is_active=payload["isActive"],
2455            is_visible=payload["visible"],
2456        )
def deserialize_item_socket( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemSocket:
2458    def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket:
2459        plug_hash: typing.Optional[int] = None
2460        if raw_plug_hash := payload.get("plugHash"):
2461            plug_hash = int(raw_plug_hash)
2462
2463        enable_fail_indexes: typing.Optional[list[int]] = None
2464        if raw_indexes := payload.get("enableFailIndexes"):
2465            enable_fail_indexes = [int(index) for index in raw_indexes]
2466
2467        return items.ItemSocket(
2468            plug_hash=plug_hash,
2469            is_enabled=payload["isEnabled"],
2470            enable_fail_indexes=enable_fail_indexes,
2471            is_visible=payload.get("visible"),
2472        )
def deserialize_item_stats_view( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemStatsView:
2474    def deserialize_item_stats_view(
2475        self, payload: typedefs.JSONObject
2476    ) -> items.ItemStatsView:
2477        return items.ItemStatsView(
2478            stat_hash=payload.get("statHash"), value=payload.get("value")
2479        )
def deserialize_plug_item_state( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.PlugItemState:
2481    def deserialize_plug_item_state(
2482        self, payload: typedefs.JSONObject
2483    ) -> items.PlugItemState:
2484        item_hash: typing.Optional[int] = None
2485        if raw_item_hash := payload.get("plugItemHash"):
2486            item_hash = int(raw_item_hash)
2487
2488        insert_fail_indexes: typedefs.NoneOr[list[int]] = None
2489        if raw_fail_indexes := payload.get("insertFailIndexes"):
2490            insert_fail_indexes = [int(k) for k in raw_fail_indexes]
2491
2492        enable_fail_indexes: typedefs.NoneOr[list[int]] = None
2493        if raw_enabled_indexes := payload.get("enableFailIndexes"):
2494            enable_fail_indexes = [int(k) for k in raw_enabled_indexes]
2495
2496        return items.PlugItemState(
2497            item_hash=item_hash,
2498            insert_fail_indexes=insert_fail_indexes,
2499            enable_fail_indexes=enable_fail_indexes,
2500            is_enabled=payload["enabled"],
2501            can_insert=payload["canInsert"],
2502        )
@typing.final
class FireteamActivity(builtins.int, aiobungie.Enum):
 68@typing.final
 69class FireteamActivity(int, enums.Enum):
 70    """An enum for the fireteam activities."""
 71
 72    ALL = 0
 73    CRUCIBLE = 2
 74    TRIALS_OF_OSIRIS = 3
 75    NIGHTFALL = 4
 76    ANY = 5
 77    GAMBIT = 6
 78    BLIND_WELL = 7
 79    NIGHTMARE_HUNTS = 12
 80    ALTARS_OF_SORROWS = 14
 81    DUNGEON = 15
 82    RAID_LW = 20
 83    RAID_GOS = 21
 84    RAID_DSC = 22
 85    EXO_CHALLENGE = 23
 86    S12_WRATHBORN = 24
 87    EMPIRE_HUNTS = 25
 88    S13_BATTLEGROUNDS = 26
 89    EXOTIC_QUEST = 27
 90    RAID_VOG = 28
 91    S14_EXPUNGE = 30
 92    S15_ASTRAL_ALIGNMENT = 31
 93    S15_SHATTERED_RELAM = 32
 94    SHATTERED_THRONE = 33
 95    PROPHECY = 34
 96    PIT_OF_HERESY = 35
 97    DOE = 36
 98    """Dares of Eternity."""
 99    DUNGEON_GOA = 37
100    """Grasp of Avarice."""
101    VOW_OF_THE_DISCPILE = 38
102    CAMPAIGN = 39
103    WELLSPRING = 40
104    S16_BATTLEGROUNDS = 41
105    S17_NIGHTMARE_CONTAINMENT = 44
106    S17_SEVER = 45

An enum for the fireteam activities.

CRUCIBLE = <FireteamActivity.CRUCIBLE: 2>
TRIALS_OF_OSIRIS = <FireteamActivity.TRIALS_OF_OSIRIS: 3>
NIGHTFALL = <FireteamActivity.NIGHTFALL: 4>
GAMBIT = <FireteamActivity.GAMBIT: 6>
BLIND_WELL = <FireteamActivity.BLIND_WELL: 7>
NIGHTMARE_HUNTS = <FireteamActivity.NIGHTMARE_HUNTS: 12>
ALTARS_OF_SORROWS = <FireteamActivity.ALTARS_OF_SORROWS: 14>
DUNGEON = <FireteamActivity.DUNGEON: 15>
RAID_LW = <FireteamActivity.RAID_LW: 20>
RAID_GOS = <FireteamActivity.RAID_GOS: 21>
RAID_DSC = <FireteamActivity.RAID_DSC: 22>
EXO_CHALLENGE = <FireteamActivity.EXO_CHALLENGE: 23>
S12_WRATHBORN = <FireteamActivity.S12_WRATHBORN: 24>
EMPIRE_HUNTS = <FireteamActivity.EMPIRE_HUNTS: 25>
S13_BATTLEGROUNDS = <FireteamActivity.S13_BATTLEGROUNDS: 26>
EXOTIC_QUEST = <FireteamActivity.EXOTIC_QUEST: 27>
RAID_VOG = <FireteamActivity.RAID_VOG: 28>
S14_EXPUNGE = <FireteamActivity.S14_EXPUNGE: 30>
S15_ASTRAL_ALIGNMENT = <FireteamActivity.S15_ASTRAL_ALIGNMENT: 31>
S15_SHATTERED_RELAM = <FireteamActivity.S15_SHATTERED_RELAM: 32>
SHATTERED_THRONE = <FireteamActivity.SHATTERED_THRONE: 33>
PROPHECY = <FireteamActivity.PROPHECY: 34>
PIT_OF_HERESY = <FireteamActivity.PIT_OF_HERESY: 35>
DOE = <FireteamActivity.DOE: 36>

Dares of Eternity.

DUNGEON_GOA = <FireteamActivity.DUNGEON_GOA: 37>

Grasp of Avarice.

VOW_OF_THE_DISCPILE = <FireteamActivity.VOW_OF_THE_DISCPILE: 38>
CAMPAIGN = <FireteamActivity.CAMPAIGN: 39>
WELLSPRING = <FireteamActivity.WELLSPRING: 40>
S16_BATTLEGROUNDS = <FireteamActivity.S16_BATTLEGROUNDS: 41>
S17_NIGHTMARE_CONTAINMENT = <FireteamActivity.S17_NIGHTMARE_CONTAINMENT: 44>
S17_SEVER = <FireteamActivity.S17_SEVER: 45>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class FireteamDate(builtins.int, aiobungie.Enum):
132@typing.final
133class FireteamDate(int, enums.Enum):
134    """An enum for fireteam date ranges."""
135
136    ALL = 0
137    NOW = 1
138    TODAY = 2
139    TWO_DAYS = 3
140    THIS_WEEK = 4

An enum for fireteam date ranges.

ALL = <FireteamDate.ALL: 0>
NOW = <FireteamDate.NOW: 1>
TODAY = <FireteamDate.TODAY: 2>
TWO_DAYS = <FireteamDate.TWO_DAYS: 3>
THIS_WEEK = <FireteamDate.THIS_WEEK: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class FireteamLanguage(builtins.str, aiobungie.Enum):
109@typing.final
110class FireteamLanguage(str, enums.Enum):
111    """An enum for fireteams languages filters."""
112
113    ALL = ""
114    ENGLISH = "en"
115    FRENCH = "fr"
116    ESPANOL = "es"
117    DEUTSCH = "de"
118    ITALIAN = "it"
119    JAPANESE = "ja"
120    PORTUGUESE = "pt-br"
121    RUSSIAN = "ru"
122    POLISH = "pl"
123    KOREAN = "ko"
124    # ? China
125    ZH_CHT = "zh-cht"
126    ZH_CHS = "zh-chs"
127
128    def __str__(self) -> str:
129        return str(self.value)

An enum for fireteams languages filters.

ENGLISH = <FireteamLanguage.ENGLISH: en>
FRENCH = <FireteamLanguage.FRENCH: fr>
ESPANOL = <FireteamLanguage.ESPANOL: es>
DEUTSCH = <FireteamLanguage.DEUTSCH: de>
ITALIAN = <FireteamLanguage.ITALIAN: it>
JAPANESE = <FireteamLanguage.JAPANESE: ja>
PORTUGUESE = <FireteamLanguage.PORTUGUESE: pt-br>
RUSSIAN = <FireteamLanguage.RUSSIAN: ru>
POLISH = <FireteamLanguage.POLISH: pl>
KOREAN = <FireteamLanguage.KOREAN: ko>
ZH_CHT = <FireteamLanguage.ZH_CHT: zh-cht>
ZH_CHS = <FireteamLanguage.ZH_CHS: zh-chs>
Inherited Members
Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@typing.final
class FireteamPlatform(builtins.int, aiobungie.Enum):
55@typing.final
56class FireteamPlatform(int, enums.Enum):
57    """An enum for fireteam related to bungie fireteams.
58    This is different from the normal `aiobungie.MembershipType`.
59    """
60
61    ANY = 0
62    PSN_NETWORK = 1
63    XBOX_LIVE = 2
64    STEAM = 4
65    STADIA = 5

An enum for fireteam related to bungie fireteams. This is different from the normal aiobungie.MembershipType.

PSN_NETWORK = <FireteamPlatform.PSN_NETWORK: 1>
XBOX_LIVE = <FireteamPlatform.XBOX_LIVE: 2>
STEAM = <FireteamPlatform.STEAM: 4>
STADIA = <FireteamPlatform.STADIA: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Flag(enum.Flag):
102class Flag(__enum.Flag):
103    """Builtin Python enum flag with extra handlings."""
104
105    # Needs to type this here for mypy
106    _value_: int
107
108    @property
109    def name(self) -> str:  # type: ignore[override]
110        if self._name_ is None:
111            self._name_ = f"UNKNOWN {self._value_}"
112
113        return self._name_
114
115    @property
116    def value(self) -> int:  # type: ignore[override]
117        return self._value_
118
119    def __str__(self) -> str:
120        return self.name
121
122    def __repr__(self) -> str:
123        return f"<{type(self).__name__}.{self.name}: {self._value_!s}>"
124
125    def __int__(self) -> int:
126        if isinstance(self.value, _ITERABLE):
127            raise TypeError(
128                f"Can't overload {self.value} in {type(self).__name__}, Please use `.value` attribute.",
129            )
130        return int(self.value)
131
132    def __or__(self, other: typing.Union[Flag, int]) -> Flag:
133        return self.__class__(self._value_ | int(other))
134
135    def __xor__(self, other: typing.Union[Flag, int]) -> Flag:
136        return self.__class__(self._value_ ^ int(other))
137
138    def __and__(self, other: typing.Union[Flag, int]) -> Flag:
139        return self.__class__(other & int(other))
140
141    def __invert__(self) -> Flag:
142        return self.__class__(~self._value_)
143
144    def __contains__(self, other: typing.Union[Flag, int]) -> bool:
145        return self.value & int(other) == int(other)

Builtin Python enum flag with extra handlings.

name: str
value: int
class FlatIterator(typing.Generic[~Item]):
 45class FlatIterator(typing.Generic[Item]):
 46    """A Flat, In-Memory iterator for sequenced based data.
 47
 48    Example
 49    -------
 50    ```py
 51    iterator = FlatIterator([1, 2, 3])
 52
 53    # Map the results.
 54    for item in iterator.map(lambda item: item * 2):
 55        print(item)
 56    # 2
 57    # 4
 58
 59    # Indexing is also supported.
 60    print(iterator[0])
 61    # 1
 62
 63    # Normal iteration.
 64    for item in iterator:
 65        print(item)
 66    # 1
 67    # 2
 68    # 3
 69
 70    # Union two iterators.
 71    iterator2 = FlatIterator([4, 5, 6])
 72    final = iterator | iterator2
 73    # <FlatIterator([1, 2, 3, 4, 5, 6])>
 74    ```
 75
 76    Parameters
 77    ----------
 78    items: `collections.Iterable[Item]`
 79        The items to iterate over.
 80    """
 81
 82    __slots__ = ("_items",)
 83
 84    def __init__(self, items: collections.Iterable[Item]) -> None:
 85        self._items = iter(items)
 86
 87    @typing.overload
 88    def collect(self) -> list[Item]:
 89        ...
 90
 91    @typing.overload
 92    def collect(self, casting: _B) -> list[_B]:
 93        ...
 94
 95    def collect(
 96        self, casting: typing.Optional[_B] = None
 97    ) -> typing.Union[list[Item], list[_B]]:
 98        """Collects all items in the iterator into a list and cast them into an object if provided.
 99
100        Example
101        -------
102        >>> iterator = FlatIterator([1, 2, 3])
103        >>> iterator.collect(casting=str)
104        ["1", "2", "3"]
105
106        Parameters
107        ----------
108        casting: `T | None`
109            The type to cast the items to. If `None` is provided, the items will be returned as is.
110
111        Raises
112        ------
113        `StopIteration`
114            If no elements are left in the iterator.
115        """
116        if casting is not None:
117            return typing.cast(list[_B], list(map(casting, self._items)))
118
119        return list(self._items)
120
121    def next(self) -> Item:
122        """Returns the next item in the iterator.
123
124        Example
125        -------
126        ```py
127        iterator = FlatIterator(["1", "2", "3"])
128        item = iterator.next()
129        assert item == "1"
130        item = iterator.next()
131        assert item == "2"
132        ```
133
134        Raises
135        ------
136        `StopIteration`
137            If no elements are left in the iterator.
138        """
139        try:
140            return self.__next__()
141        except StopIteration:
142            self._ok()
143
144    def map(
145        self, predicate: collections.Callable[[Item], OtherItem]
146    ) -> FlatIterator[OtherItem]:
147        """Maps each item in the iterator to its predicated value.
148
149        Example
150        -------
151        ```py
152        iterator = FlatIterator(["1", "2", "3"]).map(lambda value: int(value))
153        print(iterator)
154        # <FlatIterator([1, 2, 3])>
155        ```
156
157        Parameters
158        ----------
159        predicate: `collections.Callable[[Item], OtherItem]`
160            The function to map each item in the iterator to its predicated value.
161
162        Returns
163        -------
164        `FlatIterator[OtherItem]`
165            The mapped iterator.
166
167        Raises
168        ------
169        `StopIteration`
170            If no elements are left in the iterator.
171        """
172        return FlatIterator(map(predicate, self._items))
173
174    def take(self, n: int) -> FlatIterator[Item]:
175        """Take the first number of items until the number of items are yielded or
176        the end of the iterator is reached.
177
178        Example
179        -------
180        ```py
181        iterator = FlatIterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
182        print(iterator.take(2))
183        # <FlatIterator([GameMode.RAID, GameMode.STRIKE])>
184        ```
185
186        Parameters
187        ----------
188        n: `int`
189            The number of items to take.
190
191        Raises
192        ------
193        `StopIteration`
194            If no elements are left in the iterator.
195        """
196        return FlatIterator(itertools.islice(self._items, n))
197
198    def take_while(
199        self, predicate: collections.Callable[[Item], bool]
200    ) -> FlatIterator[Item]:
201        """Yields items from the iterator while predicate returns `True`.
202
203        Example
204        -------
205        ```py
206        iterator = FlatIterator([STEAM, XBOX, STADIA])
207        print(iterator.take_while(lambda platform: platform is not XBOX))
208        # <FlatIterator([STEAM])>
209        ```
210
211        Parameters
212        ----------
213        predicate: `collections.Callable[[Item], bool]`
214            The function to predicate each item in the iterator.
215
216        Raises
217        ------
218        `StopIteration`
219            If no elements are left in the iterator.
220        """
221        return FlatIterator(itertools.takewhile(predicate, self._items))
222
223    def drop_while(
224        self, predicate: collections.Callable[[Item], bool]
225    ) -> FlatIterator[Item]:
226        """Yields items from the iterator while predicate returns `False`.
227
228        Example
229        -------
230        ```py
231        iterator = FlatIterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
232        print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
233        # <FlatIterator([DestinyMembership(name="Bob")])>
234        ```
235
236        Parameters
237        ----------
238        predicate: `collections.Callable[[Item], bool]`
239            The function to predicate each item in the iterator.
240
241        Raises
242        ------
243        `StopIteration`
244            If no elements are left in the iterator.
245        """
246        return FlatIterator(itertools.dropwhile(predicate, self._items))
247
248    def filter(
249        self, predicate: collections.Callable[[Item], bool]
250    ) -> FlatIterator[Item]:
251        """Filters the iterator to only yield items that match the predicate.
252
253        Example
254        -------
255        ```py
256        names = FlatIterator(["Jim", "Bob", "Mike", "Jess"])
257        print(names.filter(lambda n: n != "Jim"))
258        # <FlatIterator(["Bob", "Mike", "Jess"])>
259        ```
260        """
261        return FlatIterator(filter(predicate, self._items))
262
263    def skip(self, n: int) -> FlatIterator[Item]:
264        """Skips the first number of items in the iterator.
265
266        Example
267        -------
268        ```py
269        iterator = FlatIterator([STEAM, XBOX, STADIA])
270        print(iterator.skip(1))
271        # <FlatIterator([XBOX, STADIA])>
272        ```
273        """
274        return FlatIterator(itertools.islice(self._items, n, None))
275
276    def discard(
277        self, predicate: collections.Callable[[Item], bool]
278    ) -> FlatIterator[Item]:
279        """Discards all elements in the iterator for which the predicate function returns true.
280
281        Example
282        -------
283        ```py
284        iterator = FlatIterator(['A', 'B', 'C'])
285        print(iterator.discard(lambda x: x == 'B'))
286        # <FlatIterator(['A', 'C'])>
287        ```
288
289        Parameters
290        ----------
291        predicate: `collections.Callable[[Item], bool]`
292            The function to test each item in the iterator.
293
294        Raises
295        ------
296        `StopIteration`
297            If no elements are left in the iterator.
298        """
299        return FlatIterator(filter(lambda x: not predicate(x), self._items))
300
301    def zip(
302        self, other: FlatIterator[OtherItem]
303    ) -> FlatIterator[tuple[Item, OtherItem]]:
304        """Zips the iterator with another iterable.
305
306        Example
307        -------
308        ```py
309        iterator = FlatIterator([1, 3, 5])
310        other = FlatIterator([2, 4, 6])
311        for item, other_item in iterator.zip(other):
312            print(item, other_item)
313        # <FlatIterator([(1, 2), (3, 4), (5, 6)])>
314        ```
315
316        Parameters
317        ----------
318        other: `FlatIterator[OtherItem]`
319            The iterable to zip with.
320
321        Raises
322        ------
323        `StopIteration`
324            If no elements are left in the iterator.
325        """
326        return FlatIterator(zip(self._items, other))
327
328    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
329        """`True` if all items in the iterator match the predicate.
330
331        Example
332        -------
333        ```py
334        iterator = FlatIterator([1, 2, 3])
335        while iterator.all(lambda item: isinstance(item, int)):
336            print("Still all integers")
337            continue
338        # Still all integers
339        ```
340
341        Parameters
342        ----------
343        predicate: `collections.Callable[[Item], bool]`
344            The function to test each item in the iterator.
345
346        Raises
347        ------
348        `StopIteration`
349            If no elements are left in the iterator.
350        """
351        return all(predicate(item) for item in self)
352
353    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
354        """`True` if any items in the iterator match the predicate.
355
356        Example
357        -------
358        ```py
359        iterator = FlatIterator([1, 2, 3])
360        if iterator.any(lambda item: isinstance(item, int)):
361            print("At least one item is an int.")
362        # At least one item is an int.
363        ```
364
365        Parameters
366        ----------
367        predicate: `collections.Callable[[Item], bool]`
368            The function to test each item in the iterator.
369
370        Raises
371        ------
372        `StopIteration`
373            If no elements are left in the iterator.
374        """
375        return any(predicate(item) for item in self)
376
377    def sort(
378        self,
379        *,
380        key: collections.Callable[[Item], typeshed.SupportsRichComparison],
381        reverse: bool = False,
382    ) -> FlatIterator[Item]:
383        """Sorts the iterator.
384
385        Example
386        -------
387        ```py
388        iterator = FlatIterator([3, 1, 6, 7])
389        print(iterator.sort(key=lambda item: item))
390        # <FlatIterator([1, 3, 6, 7])>
391        ```
392
393        Parameters
394        ----------
395        key: `collections.Callable[[Item], Any]`
396            The function to sort by.
397        reverse: `bool`
398            Whether to reverse the sort.
399
400        Raises
401        ------
402        `StopIteration`
403            If no elements are left in the iterator.
404        """
405        return FlatIterator(sorted(self._items, key=key, reverse=reverse))
406
407    def first(self) -> Item:
408        """Returns the first item in the iterator.
409
410        Example
411        -------
412        ```py
413        iterator = FlatIterator([3, 1, 6, 7])
414        print(iterator.first())
415        3
416        ```
417
418        Raises
419        ------
420        `StopIteration`
421            If no elements are left in the iterator.
422        """
423        return self.take(1).next()
424
425    def reversed(self) -> FlatIterator[Item]:
426        """Returns a new iterator that yields the items in the iterator in reverse order.
427
428        Example
429        -------
430        ```py
431        iterator = FlatIterator([3, 1, 6, 7])
432        print(iterator.reversed())
433        # <FlatIterator([7, 6, 1, 3])>
434        ```
435
436        Raises
437        ------
438        `StopIteration`
439            If no elements are left in the iterator.
440        """
441        return FlatIterator(reversed(self.collect()))
442
443    def count(self) -> int:
444        """Returns the number of items in the iterator.
445
446        Example
447        -------
448        ```py
449        iterator = FlatIterator([3, 1, 6, 7])
450        print(iterator.count())
451        4
452        ```
453        """
454        count = 0
455        for _ in self:
456            count += 1
457
458        return count
459
460    def union(self, other: FlatIterator[Item]) -> FlatIterator[Item]:
461        """Returns a new iterator that yields all items from both iterators.
462
463        Example
464        -------
465        ```py
466        iterator = FlatIterator([1, 2, 3])
467        other = FlatIterator([4, 5, 6])
468        print(iterator.union(other))
469        # <FlatIterator([1, 2, 3, 4, 5, 6])>
470        ```
471
472        Parameters
473        ----------
474        other: `FlatIterator[Item]`
475            The iterable to union with.
476
477        Raises
478        ------
479        `StopIteration`
480            If no elements are left in the iterator.
481        """
482        return FlatIterator(itertools.chain(self._items, other))
483
484    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
485        """Calls the function on each item in the iterator.
486
487        Example
488        -------
489        ```py
490        iterator = FlatIterator([1, 2, 3])
491        iterator.for_each(lambda item: print(item))
492        # 1
493        # 2
494        # 3
495        ```
496
497        Parameters
498        ----------
499        func: `typeshed.Callable[[Item], None]`
500            The function to call on each item in the iterator.
501        """
502        for item in self:
503            func(item)
504
505    async def async_for_each(
506        self, func: collections.Callable[[Item], collections.Coroutine[None, None, None]]
507    ) -> None:
508        """Calls the async function on each item in the iterator concurrently.
509
510        Example
511        -------
512        ```py
513        async def signup(username: str) -> None:
514            async with aiohttp.request('POST', '...') as r:
515                # Actual logic.
516                ...
517
518        async def main():
519            users = aiobungie.into_iter(["user_danny", "user_jojo"])
520            await users.async_for_each(lambda username: signup(username))
521        ```
522
523        Parameters
524        ----------
525        func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]`
526            The async function to call on each item in the iterator.
527        """
528        await _helpers.awaits(*(func(item) for item in self))
529
530    def enumerate(self, *, start: int = 0) -> FlatIterator[tuple[int, Item]]:
531        """Returns a new iterator that yields tuples of the index and item.
532
533        Example
534        -------
535        ```py
536        iterator = FlatIterator([1, 2, 3])
537        for index, item in iterator.enumerate():
538            print(index, item)
539        # 0 1
540        # 1 2
541        # 2 3
542        ```
543
544        Raises
545        ------
546        `StopIteration`
547            If no elements are left in the iterator.
548        """
549        return FlatIterator(enumerate(self._items, start=start))
550
551    def _ok(self) -> typing.NoReturn:
552        raise StopIteration("No more items in the iterator.") from None
553
554    def __getitem__(self, index: int) -> Item:
555        try:
556            return self.skip(index).first()
557        except IndexError:
558            self._ok()
559
560    def __or__(self, other: FlatIterator[Item]) -> FlatIterator[Item]:
561        return self.union(other)
562
563    # This is a never.
564    def __setitem__(self) -> typing.NoReturn:
565        raise TypeError(
566            f"{type(self).__name__} doesn't support item assignment."
567        ) from None
568
569    def __repr__(self) -> str:
570        return f'<{self.__class__.__name__}({", ".join([str(item) for item in self])})>'
571
572    def __len__(self) -> int:
573        return self.count()
574
575    def __iter__(self) -> FlatIterator[Item]:
576        return self
577
578    def __next__(self) -> Item:
579        try:
580            item = next(self._items)
581        except StopIteration:
582            self._ok()
583
584        return item

A Flat, In-Memory iterator for sequenced based data.

Example
iterator = FlatIterator([1, 2, 3])

# Map the results.
for item in iterator.map(lambda item: item * 2):
    print(item)
# 2
# 4

# Indexing is also supported.
print(iterator[0])
# 1

# Normal iteration.
for item in iterator:
    print(item)
# 1
# 2
# 3

# Union two iterators.
iterator2 = FlatIterator([4, 5, 6])
final = iterator | iterator2
# <FlatIterator([1, 2, 3, 4, 5, 6])>
Parameters
  • items (collections.Iterable[Item]): The items to iterate over.
FlatIterator(items: collections.abc.Iterable[~Item])
84    def __init__(self, items: collections.Iterable[Item]) -> None:
85        self._items = iter(items)
def collect( self, casting: 'typing.Optional[_B]' = None) -> 'typing.Union[list[Item], list[_B]]':
 95    def collect(
 96        self, casting: typing.Optional[_B] = None
 97    ) -> typing.Union[list[Item], list[_B]]:
 98        """Collects all items in the iterator into a list and cast them into an object if provided.
 99
100        Example
101        -------
102        >>> iterator = FlatIterator([1, 2, 3])
103        >>> iterator.collect(casting=str)
104        ["1", "2", "3"]
105
106        Parameters
107        ----------
108        casting: `T | None`
109            The type to cast the items to. If `None` is provided, the items will be returned as is.
110
111        Raises
112        ------
113        `StopIteration`
114            If no elements are left in the iterator.
115        """
116        if casting is not None:
117            return typing.cast(list[_B], list(map(casting, self._items)))
118
119        return list(self._items)

Collects all items in the iterator into a list and cast them into an object if provided.

Example
>>> iterator = FlatIterator([1, 2, 3])
>>> iterator.collect(casting=str)
["1", "2", "3"]
Parameters
  • casting (T | None): The type to cast the items to. If None is provided, the items will be returned as is.
Raises
  • StopIteration: If no elements are left in the iterator.
def next(self) -> ~Item:
121    def next(self) -> Item:
122        """Returns the next item in the iterator.
123
124        Example
125        -------
126        ```py
127        iterator = FlatIterator(["1", "2", "3"])
128        item = iterator.next()
129        assert item == "1"
130        item = iterator.next()
131        assert item == "2"
132        ```
133
134        Raises
135        ------
136        `StopIteration`
137            If no elements are left in the iterator.
138        """
139        try:
140            return self.__next__()
141        except StopIteration:
142            self._ok()

Returns the next item in the iterator.

Example
iterator = FlatIterator(["1", "2", "3"])
item = iterator.next()
assert item == "1"
item = iterator.next()
assert item == "2"
Raises
  • StopIteration: If no elements are left in the iterator.
def map( self, predicate: 'collections.Callable[[Item], OtherItem]') -> 'FlatIterator[OtherItem]':
144    def map(
145        self, predicate: collections.Callable[[Item], OtherItem]
146    ) -> FlatIterator[OtherItem]:
147        """Maps each item in the iterator to its predicated value.
148
149        Example
150        -------
151        ```py
152        iterator = FlatIterator(["1", "2", "3"]).map(lambda value: int(value))
153        print(iterator)
154        # <FlatIterator([1, 2, 3])>
155        ```
156
157        Parameters
158        ----------
159        predicate: `collections.Callable[[Item], OtherItem]`
160            The function to map each item in the iterator to its predicated value.
161
162        Returns
163        -------
164        `FlatIterator[OtherItem]`
165            The mapped iterator.
166
167        Raises
168        ------
169        `StopIteration`
170            If no elements are left in the iterator.
171        """
172        return FlatIterator(map(predicate, self._items))

Maps each item in the iterator to its predicated value.

Example
iterator = FlatIterator(["1", "2", "3"]).map(lambda value: int(value))
print(iterator)
# <FlatIterator([1, 2, 3])>
Parameters
  • predicate (collections.Callable[[Item], OtherItem]): The function to map each item in the iterator to its predicated value.
Returns
  • FlatIterator[OtherItem]: The mapped iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def take(self, n: int) -> aiobungie.FlatIterator[~Item]:
174    def take(self, n: int) -> FlatIterator[Item]:
175        """Take the first number of items until the number of items are yielded or
176        the end of the iterator is reached.
177
178        Example
179        -------
180        ```py
181        iterator = FlatIterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
182        print(iterator.take(2))
183        # <FlatIterator([GameMode.RAID, GameMode.STRIKE])>
184        ```
185
186        Parameters
187        ----------
188        n: `int`
189            The number of items to take.
190
191        Raises
192        ------
193        `StopIteration`
194            If no elements are left in the iterator.
195        """
196        return FlatIterator(itertools.islice(self._items, n))

Take the first number of items until the number of items are yielded or the end of the iterator is reached.

Example
iterator = FlatIterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
print(iterator.take(2))
# <FlatIterator([GameMode.RAID, GameMode.STRIKE])>
Parameters
  • n (int): The number of items to take.
Raises
  • StopIteration: If no elements are left in the iterator.
def take_while( self, predicate: collections.abc.Callable[[~Item], bool]) -> aiobungie.FlatIterator[~Item]:
198    def take_while(
199        self, predicate: collections.Callable[[Item], bool]
200    ) -> FlatIterator[Item]:
201        """Yields items from the iterator while predicate returns `True`.
202
203        Example
204        -------
205        ```py
206        iterator = FlatIterator([STEAM, XBOX, STADIA])
207        print(iterator.take_while(lambda platform: platform is not XBOX))
208        # <FlatIterator([STEAM])>
209        ```
210
211        Parameters
212        ----------
213        predicate: `collections.Callable[[Item], bool]`
214            The function to predicate each item in the iterator.
215
216        Raises
217        ------
218        `StopIteration`
219            If no elements are left in the iterator.
220        """
221        return FlatIterator(itertools.takewhile(predicate, self._items))

Yields items from the iterator while predicate returns True.

Example
iterator = FlatIterator([STEAM, XBOX, STADIA])
print(iterator.take_while(lambda platform: platform is not XBOX))
# <FlatIterator([STEAM])>
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def drop_while( self, predicate: collections.abc.Callable[[~Item], bool]) -> aiobungie.FlatIterator[~Item]:
223    def drop_while(
224        self, predicate: collections.Callable[[Item], bool]
225    ) -> FlatIterator[Item]:
226        """Yields items from the iterator while predicate returns `False`.
227
228        Example
229        -------
230        ```py
231        iterator = FlatIterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
232        print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
233        # <FlatIterator([DestinyMembership(name="Bob")])>
234        ```
235
236        Parameters
237        ----------
238        predicate: `collections.Callable[[Item], bool]`
239            The function to predicate each item in the iterator.
240
241        Raises
242        ------
243        `StopIteration`
244            If no elements are left in the iterator.
245        """
246        return FlatIterator(itertools.dropwhile(predicate, self._items))

Yields items from the iterator while predicate returns False.

Example
iterator = FlatIterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
# <FlatIterator([DestinyMembership(name="Bob")])>
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def filter( self, predicate: collections.abc.Callable[[~Item], bool]) -> aiobungie.FlatIterator[~Item]:
248    def filter(
249        self, predicate: collections.Callable[[Item], bool]
250    ) -> FlatIterator[Item]:
251        """Filters the iterator to only yield items that match the predicate.
252
253        Example
254        -------
255        ```py
256        names = FlatIterator(["Jim", "Bob", "Mike", "Jess"])
257        print(names.filter(lambda n: n != "Jim"))
258        # <FlatIterator(["Bob", "Mike", "Jess"])>
259        ```
260        """
261        return FlatIterator(filter(predicate, self._items))

Filters the iterator to only yield items that match the predicate.

Example
names = FlatIterator(["Jim", "Bob", "Mike", "Jess"])
print(names.filter(lambda n: n != "Jim"))
# <FlatIterator(["Bob", "Mike", "Jess"])>
def skip(self, n: int) -> aiobungie.FlatIterator[~Item]:
263    def skip(self, n: int) -> FlatIterator[Item]:
264        """Skips the first number of items in the iterator.
265
266        Example
267        -------
268        ```py
269        iterator = FlatIterator([STEAM, XBOX, STADIA])
270        print(iterator.skip(1))
271        # <FlatIterator([XBOX, STADIA])>
272        ```
273        """
274        return FlatIterator(itertools.islice(self._items, n, None))

Skips the first number of items in the iterator.

Example
iterator = FlatIterator([STEAM, XBOX, STADIA])
print(iterator.skip(1))
# <FlatIterator([XBOX, STADIA])>
def discard( self, predicate: collections.abc.Callable[[~Item], bool]) -> aiobungie.FlatIterator[~Item]:
276    def discard(
277        self, predicate: collections.Callable[[Item], bool]
278    ) -> FlatIterator[Item]:
279        """Discards all elements in the iterator for which the predicate function returns true.
280
281        Example
282        -------
283        ```py
284        iterator = FlatIterator(['A', 'B', 'C'])
285        print(iterator.discard(lambda x: x == 'B'))
286        # <FlatIterator(['A', 'C'])>
287        ```
288
289        Parameters
290        ----------
291        predicate: `collections.Callable[[Item], bool]`
292            The function to test each item in the iterator.
293
294        Raises
295        ------
296        `StopIteration`
297            If no elements are left in the iterator.
298        """
299        return FlatIterator(filter(lambda x: not predicate(x), self._items))

Discards all elements in the iterator for which the predicate function returns true.

Example
iterator = FlatIterator(['A', 'B', 'C'])
print(iterator.discard(lambda x: x == 'B'))
# <FlatIterator(['A', 'C'])>
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def zip( self, other: 'FlatIterator[OtherItem]') -> 'FlatIterator[tuple[Item, OtherItem]]':
301    def zip(
302        self, other: FlatIterator[OtherItem]
303    ) -> FlatIterator[tuple[Item, OtherItem]]:
304        """Zips the iterator with another iterable.
305
306        Example
307        -------
308        ```py
309        iterator = FlatIterator([1, 3, 5])
310        other = FlatIterator([2, 4, 6])
311        for item, other_item in iterator.zip(other):
312            print(item, other_item)
313        # <FlatIterator([(1, 2), (3, 4), (5, 6)])>
314        ```
315
316        Parameters
317        ----------
318        other: `FlatIterator[OtherItem]`
319            The iterable to zip with.
320
321        Raises
322        ------
323        `StopIteration`
324            If no elements are left in the iterator.
325        """
326        return FlatIterator(zip(self._items, other))

Zips the iterator with another iterable.

Example
iterator = FlatIterator([1, 3, 5])
other = FlatIterator([2, 4, 6])
for item, other_item in iterator.zip(other):
    print(item, other_item)
# <FlatIterator([(1, 2), (3, 4), (5, 6)])>
Parameters
  • other (FlatIterator[OtherItem]): The iterable to zip with.
Raises
  • StopIteration: If no elements are left in the iterator.
def all(self, predicate: collections.abc.Callable[[~Item], bool]) -> bool:
328    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
329        """`True` if all items in the iterator match the predicate.
330
331        Example
332        -------
333        ```py
334        iterator = FlatIterator([1, 2, 3])
335        while iterator.all(lambda item: isinstance(item, int)):
336            print("Still all integers")
337            continue
338        # Still all integers
339        ```
340
341        Parameters
342        ----------
343        predicate: `collections.Callable[[Item], bool]`
344            The function to test each item in the iterator.
345
346        Raises
347        ------
348        `StopIteration`
349            If no elements are left in the iterator.
350        """
351        return all(predicate(item) for item in self)

True if all items in the iterator match the predicate.

Example
iterator = FlatIterator([1, 2, 3])
while iterator.all(lambda item: isinstance(item, int)):
    print("Still all integers")
    continue
# Still all integers
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def any(self, predicate: collections.abc.Callable[[~Item], bool]) -> bool:
353    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
354        """`True` if any items in the iterator match the predicate.
355
356        Example
357        -------
358        ```py
359        iterator = FlatIterator([1, 2, 3])
360        if iterator.any(lambda item: isinstance(item, int)):
361            print("At least one item is an int.")
362        # At least one item is an int.
363        ```
364
365        Parameters
366        ----------
367        predicate: `collections.Callable[[Item], bool]`
368            The function to test each item in the iterator.
369
370        Raises
371        ------
372        `StopIteration`
373            If no elements are left in the iterator.
374        """
375        return any(predicate(item) for item in self)

True if any items in the iterator match the predicate.

Example
iterator = FlatIterator([1, 2, 3])
if iterator.any(lambda item: isinstance(item, int)):
    print("At least one item is an int.")
# At least one item is an int.
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def sort( self, *, key: 'collections.Callable[[Item], typeshed.SupportsRichComparison]', reverse: bool = False) -> aiobungie.FlatIterator[~Item]:
377    def sort(
378        self,
379        *,
380        key: collections.Callable[[Item], typeshed.SupportsRichComparison],
381        reverse: bool = False,
382    ) -> FlatIterator[Item]:
383        """Sorts the iterator.
384
385        Example
386        -------
387        ```py
388        iterator = FlatIterator([3, 1, 6, 7])
389        print(iterator.sort(key=lambda item: item))
390        # <FlatIterator([1, 3, 6, 7])>
391        ```
392
393        Parameters
394        ----------
395        key: `collections.Callable[[Item], Any]`
396            The function to sort by.
397        reverse: `bool`
398            Whether to reverse the sort.
399
400        Raises
401        ------
402        `StopIteration`
403            If no elements are left in the iterator.
404        """
405        return FlatIterator(sorted(self._items, key=key, reverse=reverse))

Sorts the iterator.

Example
iterator = FlatIterator([3, 1, 6, 7])
print(iterator.sort(key=lambda item: item))
# <FlatIterator([1, 3, 6, 7])>
Parameters
  • key (collections.Callable[[Item], Any]): The function to sort by.
  • reverse (bool): Whether to reverse the sort.
Raises
  • StopIteration: If no elements are left in the iterator.
def first(self) -> ~Item:
407    def first(self) -> Item:
408        """Returns the first item in the iterator.
409
410        Example
411        -------
412        ```py
413        iterator = FlatIterator([3, 1, 6, 7])
414        print(iterator.first())
415        3
416        ```
417
418        Raises
419        ------
420        `StopIteration`
421            If no elements are left in the iterator.
422        """
423        return self.take(1).next()

Returns the first item in the iterator.

Example
iterator = FlatIterator([3, 1, 6, 7])
print(iterator.first())
3
Raises
  • StopIteration: If no elements are left in the iterator.
def reversed(self) -> aiobungie.FlatIterator[~Item]:
425    def reversed(self) -> FlatIterator[Item]:
426        """Returns a new iterator that yields the items in the iterator in reverse order.
427
428        Example
429        -------
430        ```py
431        iterator = FlatIterator([3, 1, 6, 7])
432        print(iterator.reversed())
433        # <FlatIterator([7, 6, 1, 3])>
434        ```
435
436        Raises
437        ------
438        `StopIteration`
439            If no elements are left in the iterator.
440        """
441        return FlatIterator(reversed(self.collect()))

Returns a new iterator that yields the items in the iterator in reverse order.

Example
iterator = FlatIterator([3, 1, 6, 7])
print(iterator.reversed())
# <FlatIterator([7, 6, 1, 3])>
Raises
  • StopIteration: If no elements are left in the iterator.
def count(self) -> int:
443    def count(self) -> int:
444        """Returns the number of items in the iterator.
445
446        Example
447        -------
448        ```py
449        iterator = FlatIterator([3, 1, 6, 7])
450        print(iterator.count())
451        4
452        ```
453        """
454        count = 0
455        for _ in self:
456            count += 1
457
458        return count

Returns the number of items in the iterator.

Example
iterator = FlatIterator([3, 1, 6, 7])
print(iterator.count())
4
def union( self, other: aiobungie.FlatIterator[~Item]) -> aiobungie.FlatIterator[~Item]:
460    def union(self, other: FlatIterator[Item]) -> FlatIterator[Item]:
461        """Returns a new iterator that yields all items from both iterators.
462
463        Example
464        -------
465        ```py
466        iterator = FlatIterator([1, 2, 3])
467        other = FlatIterator([4, 5, 6])
468        print(iterator.union(other))
469        # <FlatIterator([1, 2, 3, 4, 5, 6])>
470        ```
471
472        Parameters
473        ----------
474        other: `FlatIterator[Item]`
475            The iterable to union with.
476
477        Raises
478        ------
479        `StopIteration`
480            If no elements are left in the iterator.
481        """
482        return FlatIterator(itertools.chain(self._items, other))

Returns a new iterator that yields all items from both iterators.

Example
iterator = FlatIterator([1, 2, 3])
other = FlatIterator([4, 5, 6])
print(iterator.union(other))
# <FlatIterator([1, 2, 3, 4, 5, 6])>
Parameters
  • other (FlatIterator[Item]): The iterable to union with.
Raises
  • StopIteration: If no elements are left in the iterator.
def for_each(self, func: collections.abc.Callable[[~Item], typing.Any]) -> None:
484    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
485        """Calls the function on each item in the iterator.
486
487        Example
488        -------
489        ```py
490        iterator = FlatIterator([1, 2, 3])
491        iterator.for_each(lambda item: print(item))
492        # 1
493        # 2
494        # 3
495        ```
496
497        Parameters
498        ----------
499        func: `typeshed.Callable[[Item], None]`
500            The function to call on each item in the iterator.
501        """
502        for item in self:
503            func(item)

Calls the function on each item in the iterator.

Example
iterator = FlatIterator([1, 2, 3])
iterator.for_each(lambda item: print(item))
# 1
# 2
# 3
Parameters
  • func (typeshed.Callable[[Item], None]): The function to call on each item in the iterator.
async def async_for_each( self, func: collections.abc.Callable[[~Item], collections.abc.Coroutine[None, None, None]]) -> None:
505    async def async_for_each(
506        self, func: collections.Callable[[Item], collections.Coroutine[None, None, None]]
507    ) -> None:
508        """Calls the async function on each item in the iterator concurrently.
509
510        Example
511        -------
512        ```py
513        async def signup(username: str) -> None:
514            async with aiohttp.request('POST', '...') as r:
515                # Actual logic.
516                ...
517
518        async def main():
519            users = aiobungie.into_iter(["user_danny", "user_jojo"])
520            await users.async_for_each(lambda username: signup(username))
521        ```
522
523        Parameters
524        ----------
525        func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]`
526            The async function to call on each item in the iterator.
527        """
528        await _helpers.awaits(*(func(item) for item in self))

Calls the async function on each item in the iterator concurrently.

Example
async def signup(username: str) -> None:
    async with aiohttp.request('POST', '...') as r:
        # Actual logic.
        ...

async def main():
    users = aiobungie.into_iter(["user_danny", "user_jojo"])
    await users.async_for_each(lambda username: signup(username))
Parameters
  • func (collections.Callable[[Item], collections.Coroutine[None, None, None]]): The async function to call on each item in the iterator.
def enumerate( self, *, start: int = 0) -> aiobungie.FlatIterator[tuple[int, ~Item]]:
530    def enumerate(self, *, start: int = 0) -> FlatIterator[tuple[int, Item]]:
531        """Returns a new iterator that yields tuples of the index and item.
532
533        Example
534        -------
535        ```py
536        iterator = FlatIterator([1, 2, 3])
537        for index, item in iterator.enumerate():
538            print(index, item)
539        # 0 1
540        # 1 2
541        # 2 3
542        ```
543
544        Raises
545        ------
546        `StopIteration`
547            If no elements are left in the iterator.
548        """
549        return FlatIterator(enumerate(self._items, start=start))

Returns a new iterator that yields tuples of the index and item.

Example
iterator = FlatIterator([1, 2, 3])
for index, item in iterator.enumerate():
    print(index, item)
# 0 1
# 1 2
# 2 3
Raises
  • StopIteration: If no elements are left in the iterator.
@attrs.define(auto_exc=True)
class Forbidden(aiobungie.HTTPException):
120@attrs.define(auto_exc=True)
121class Forbidden(HTTPException):
122    """Exception that's raised for when status code 403 occurs."""
123
124    http_status: http.HTTPStatus = attrs.field(
125        default=http.HTTPStatus.FORBIDDEN, init=False
126    )

Exception that's raised for when status code 403 occurs.

Forbidden( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class Forbidden.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class GameMode(builtins.int, aiobungie.Enum):
274@typing.final
275class GameMode(int, Enum):
276    """An Enum for all available gamemodes in Destiny 2."""
277
278    NONE = 0
279    STORY = 2
280    STRIKE = 3
281    RAID = 4
282    ALLPVP = 5
283    PATROL = 6
284    ALLPVE = 7
285    RESERVED9 = 9
286    CONTROL = 10
287    RESERVED11 = 11
288    CLASH = 12
289    RESERVED13 = 13
290    CRIMSONDOUBLES = 15
291    NIGHTFALL = 16
292    HEROICNIGHTFALL = 17
293    ALLSTRIKES = 18
294    IRONBANNER = 19
295    RESERVED20 = 20
296    RESERVED21 = 21
297    RESERVED22 = 22
298    RESERVED24 = 24
299    ALLMAYHEM = 25
300    RESERVED26 = 26
301    RESERVED27 = 27
302    RESERVED28 = 28
303    RESERVED29 = 29
304    RESERVED30 = 30
305    SUPREMACY = 31
306    PRIVATEMATCHESALL = 32
307    SURVIVAL = 37
308    COUNTDOWN = 38
309    TRIALSOFTHENINE = 39
310    SOCIAL = 40
311    TRIALSCOUNTDOWN = 41
312    TRIALSSURVIVAL = 42
313    IRONBANNERCONTROL = 43
314    IRONBANNERCLASH = 44
315    IRONBANNERSUPREMACY = 45
316    SCOREDNIGHTFALL = 46
317    SCOREDHEROICNIGHTFALL = 47
318    RUMBLE = 48
319    ALLDOUBLES = 49
320    DOUBLES = 50
321    PRIVATEMATCHESCLASH = 51
322    PRIVATEMATCHESCONTROL = 52
323    PRIVATEMATCHESSUPREMACY = 53
324    PRIVATEMATCHESCOUNTDOWN = 54
325    PRIVATEMATCHESSURVIVAL = 55
326    PRIVATEMATCHESMAYHEM = 56
327    PRIVATEMATCHESRUMBLE = 57
328    HEROICADVENTURE = 58
329    SHOWDOWN = 59
330    LOCKDOWN = 60
331    SCORCHED = 61
332    SCORCHEDTEAM = 62
333    GAMBIT = 63
334    ALLPVECOMPETITIVE = 64
335    BREAKTHROUGH = 65
336    BLACKARMORYRUN = 66
337    SALVAGE = 67
338    IRONBANNERSALVAGE = 68
339    PVPCOMPETITIVE = 69
340    PVPQUICKPLAY = 70
341    CLASHQUICKPLAY = 71
342    CLASHCOMPETITIVE = 72
343    CONTROLQUICKPLAY = 73
344    CONTROLCOMPETITIVE = 74
345    GAMBITPRIME = 75
346    RECKONING = 76
347    MENAGERIE = 77
348    VEXOFFENSIVE = 78
349    NIGHTMAREHUNT = 79
350    ELIMINATION = 80
351    MOMENTUM = 81
352    DUNGEON = 82
353    SUNDIAL = 83
354    TRIALS_OF_OSIRIS = 84
355    DARES = 85
356    OFFENSIVE = 86
357    LOSTSECTOR = 87
358    RIFT = 88
359    ZONECONTROL = 89
360    IRONBANNERRIFT = 90

An Enum for all available gamemodes in Destiny 2.

NONE = <GameMode.NONE: 0>
STORY = <GameMode.STORY: 2>
STRIKE = <GameMode.STRIKE: 3>
RAID = <GameMode.RAID: 4>
ALLPVP = <GameMode.ALLPVP: 5>
PATROL = <GameMode.PATROL: 6>
ALLPVE = <GameMode.ALLPVE: 7>
RESERVED9 = <GameMode.RESERVED9: 9>
CONTROL = <GameMode.CONTROL: 10>
RESERVED11 = <GameMode.RESERVED11: 11>
CLASH = <GameMode.CLASH: 12>
RESERVED13 = <GameMode.RESERVED13: 13>
CRIMSONDOUBLES = <GameMode.CRIMSONDOUBLES: 15>
NIGHTFALL = <GameMode.NIGHTFALL: 16>
HEROICNIGHTFALL = <GameMode.HEROICNIGHTFALL: 17>
ALLSTRIKES = <GameMode.ALLSTRIKES: 18>
IRONBANNER = <GameMode.IRONBANNER: 19>
RESERVED20 = <GameMode.RESERVED20: 20>
RESERVED21 = <GameMode.RESERVED21: 21>
RESERVED22 = <GameMode.RESERVED22: 22>
RESERVED24 = <GameMode.RESERVED24: 24>
ALLMAYHEM = <GameMode.ALLMAYHEM: 25>
RESERVED26 = <GameMode.RESERVED26: 26>
RESERVED27 = <GameMode.RESERVED27: 27>
RESERVED28 = <GameMode.RESERVED28: 28>
RESERVED29 = <GameMode.RESERVED29: 29>
RESERVED30 = <GameMode.RESERVED30: 30>
SUPREMACY = <GameMode.SUPREMACY: 31>
PRIVATEMATCHESALL = <GameMode.PRIVATEMATCHESALL: 32>
SURVIVAL = <GameMode.SURVIVAL: 37>
COUNTDOWN = <GameMode.COUNTDOWN: 38>
TRIALSOFTHENINE = <GameMode.TRIALSOFTHENINE: 39>
SOCIAL = <GameMode.SOCIAL: 40>
TRIALSCOUNTDOWN = <GameMode.TRIALSCOUNTDOWN: 41>
TRIALSSURVIVAL = <GameMode.TRIALSSURVIVAL: 42>
IRONBANNERCONTROL = <GameMode.IRONBANNERCONTROL: 43>
IRONBANNERCLASH = <GameMode.IRONBANNERCLASH: 44>
IRONBANNERSUPREMACY = <GameMode.IRONBANNERSUPREMACY: 45>
SCOREDNIGHTFALL = <GameMode.SCOREDNIGHTFALL: 46>
SCOREDHEROICNIGHTFALL = <GameMode.SCOREDHEROICNIGHTFALL: 47>
RUMBLE = <GameMode.RUMBLE: 48>
ALLDOUBLES = <GameMode.ALLDOUBLES: 49>
DOUBLES = <GameMode.DOUBLES: 50>
PRIVATEMATCHESCLASH = <GameMode.PRIVATEMATCHESCLASH: 51>
PRIVATEMATCHESCONTROL = <GameMode.PRIVATEMATCHESCONTROL: 52>
PRIVATEMATCHESSUPREMACY = <GameMode.PRIVATEMATCHESSUPREMACY: 53>
PRIVATEMATCHESCOUNTDOWN = <GameMode.PRIVATEMATCHESCOUNTDOWN: 54>
PRIVATEMATCHESSURVIVAL = <GameMode.PRIVATEMATCHESSURVIVAL: 55>
PRIVATEMATCHESMAYHEM = <GameMode.PRIVATEMATCHESMAYHEM: 56>
PRIVATEMATCHESRUMBLE = <GameMode.PRIVATEMATCHESRUMBLE: 57>
HEROICADVENTURE = <GameMode.HEROICADVENTURE: 58>
SHOWDOWN = <GameMode.SHOWDOWN: 59>
LOCKDOWN = <GameMode.LOCKDOWN: 60>
SCORCHED = <GameMode.SCORCHED: 61>
SCORCHEDTEAM = <GameMode.SCORCHEDTEAM: 62>
GAMBIT = <GameMode.GAMBIT: 63>
ALLPVECOMPETITIVE = <GameMode.ALLPVECOMPETITIVE: 64>
BREAKTHROUGH = <GameMode.BREAKTHROUGH: 65>
BLACKARMORYRUN = <GameMode.BLACKARMORYRUN: 66>
SALVAGE = <GameMode.SALVAGE: 67>
IRONBANNERSALVAGE = <GameMode.IRONBANNERSALVAGE: 68>
PVPCOMPETITIVE = <GameMode.PVPCOMPETITIVE: 69>
PVPQUICKPLAY = <GameMode.PVPQUICKPLAY: 70>
CLASHQUICKPLAY = <GameMode.CLASHQUICKPLAY: 71>
CLASHCOMPETITIVE = <GameMode.CLASHCOMPETITIVE: 72>
CONTROLQUICKPLAY = <GameMode.CONTROLQUICKPLAY: 73>
CONTROLCOMPETITIVE = <GameMode.CONTROLCOMPETITIVE: 74>
GAMBITPRIME = <GameMode.GAMBITPRIME: 75>
RECKONING = <GameMode.RECKONING: 76>
MENAGERIE = <GameMode.MENAGERIE: 77>
VEXOFFENSIVE = <GameMode.VEXOFFENSIVE: 78>
NIGHTMAREHUNT = <GameMode.NIGHTMAREHUNT: 79>
ELIMINATION = <GameMode.ELIMINATION: 80>
MOMENTUM = <GameMode.MOMENTUM: 81>
DUNGEON = <GameMode.DUNGEON: 82>
SUNDIAL = <GameMode.SUNDIAL: 83>
TRIALS_OF_OSIRIS = <GameMode.TRIALS_OF_OSIRIS: 84>
DARES = <GameMode.DARES: 85>
OFFENSIVE = <GameMode.OFFENSIVE: 86>
LOSTSECTOR = <GameMode.LOSTSECTOR: 87>
RIFT = <GameMode.RIFT: 88>
ZONECONTROL = <GameMode.ZONECONTROL: 89>
IRONBANNERRIFT = <GameMode.IRONBANNERRIFT: 90>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class GatingScope(builtins.int, aiobungie.Enum):
58@typing.final
59class GatingScope(int, enums.Enum):
60    """An enum represents restrictive type of gating that is being performed by an entity.
61
62    This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity
63    applies to everyone equally, or to their specific Profile or Character states.
64    """
65
66    NONE = 0
67    GLOBAL = 1
68    CLAN = 2
69    PROFILE = 3
70    CHARACTER = 4
71    ITEM = 5
72    ASSUMED_WORST_CASE = 6

An enum represents restrictive type of gating that is being performed by an entity.

This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity applies to everyone equally, or to their specific Profile or Character states.

NONE = <GatingScope.NONE: 0>
GLOBAL = <GatingScope.GLOBAL: 1>
CLAN = <GatingScope.CLAN: 2>
PROFILE = <GatingScope.PROFILE: 3>
CHARACTER = <GatingScope.CHARACTER: 4>
ITEM = <GatingScope.ITEM: 5>
ASSUMED_WORST_CASE = <GatingScope.ASSUMED_WORST_CASE: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Gender(builtins.int, aiobungie.Enum):
487@typing.final
488class Gender(int, Enum):
489    """An Enum for Destiny Genders."""
490
491    MALE = 0
492    FEMALE = 1
493    UNKNOWN = 2

An Enum for Destiny Genders.

MALE = <Gender.MALE: 0>
FEMALE = <Gender.FEMALE: 1>
UNKNOWN = <Gender.UNKNOWN: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class GroupType(builtins.int, aiobungie.Enum):
656@typing.final
657class GroupType(int, Enum):
658    """An enums for the known bungie group types."""
659
660    GENERAL = 0
661    CLAN = 1

An enums for the known bungie group types.

GENERAL = <GroupType.GENERAL: 0>
CLAN = <GroupType.CLAN: 1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class HTTPError(aiobungie.AiobungieError):
62@attrs.define(auto_exc=True)
63class HTTPError(AiobungieError):
64    """Exception base used for HTTP request errors."""
65
66    message: str
67    """The error message."""
68
69    http_status: http.HTTPStatus
70    """The response status."""

Exception base used for HTTP request errors.

HTTPError(message: str, http_status: http.HTTPStatus)
2def __init__(self, message, http_status):
3    self.message = message
4    self.http_status = http_status
5    BaseException.__init__(self, self.message,self.http_status)

Method generated by attrs for class HTTPError.

message: str

The error message.

http_status: http.HTTPStatus

The response status.

Inherited Members
builtins.BaseException
with_traceback
args
@attrs.define(auto_exc=True, kw_only=True)
class HTTPException(aiobungie.HTTPError):
 73@attrs.define(auto_exc=True, kw_only=True)
 74class HTTPException(HTTPError):
 75    """Exception base internally used for an HTTP request response errors."""
 76
 77    error_code: int
 78    """The returned Bungie error status code."""
 79
 80    http_status: http.HTTPStatus
 81    """The request response http status."""
 82
 83    throttle_seconds: int
 84    """The Bungie response throttle seconds."""
 85
 86    url: typing.Optional[typedefs.StrOrURL]
 87    """The URL/endpoint caused this error."""
 88
 89    body: typing.Any
 90    """The response body."""
 91
 92    headers: multidict.CIMultiDictProxy[str]
 93    """The response headers."""
 94
 95    message: str
 96    """A Bungie human readable message describes the cause of the error."""
 97
 98    error_status: str
 99    """A Bungie short error status describes the cause of the error."""
100
101    message_data: dict[str, str]
102    """A dict of string key, value that includes each cause of the error
103    to a message describes information about that error.
104    """
105
106    def __str__(self) -> str:
107        if self.message:
108            message_body = self.message
109
110        if self.error_status:
111            error_status_body = self.error_status
112
113        return (
114            f"{self.http_status.name.replace('_', '').title()} {self.http_status.value}: "
115            f"Error status: {error_status_body}, Error message: {message_body} from {self.url} "
116            f"{str(self.body)}"
117        )

Exception base internally used for an HTTP request response errors.

HTTPException( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class HTTPException.

error_code: int

The returned Bungie error status code.

http_status: http.HTTPStatus

The request response http status.

throttle_seconds: int

The Bungie response throttle seconds.

url: Union[str, yarl.URL, NoneType]

The URL/endpoint caused this error.

body: Any

The response body.

headers: multidict._multidict.CIMultiDictProxy[str]

The response headers.

message: str

A Bungie human readable message describes the cause of the error.

error_status: str

A Bungie short error status describes the cause of the error.

message_data: dict[str, str]

A dict of string key, value that includes each cause of the error to a message describes information about that error.

Inherited Members
builtins.BaseException
with_traceback
args
class Image:
 72class Image:
 73    """Representation of an image/avatar/picture at Bungie.
 74
 75    Example
 76    -------
 77    ```py
 78    from aiobungie import Image
 79    img = Image("img/destiny_content/pgcr/raid_eclipse.jpg")
 80    print(img)
 81    # https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg
 82
 83    # Stream the image.
 84    async for chunk in img:
 85        # Byte chunks of the image.
 86        print(chunk)
 87
 88    # Save the image to a file.
 89    await img.save("file_name", "/my/path/to/save/to", "jpeg")
 90    ```
 91
 92    Parameters
 93    ----------
 94    path : `str | None`
 95        The path to the image. If `None`, the default missing image path will be used.
 96    """
 97
 98    __slots__ = ("_path",)
 99
100    def __init__(self, path: typing.Optional[str] = None) -> None:
101        self._path = path
102
103    @property
104    def is_missing(self) -> bool:
105        return not self._path
106
107    @property
108    def url(self) -> str:
109        """The URL to the image."""
110        return self.create_url()
111
112    @staticmethod
113    def missing_path() -> str:
114        """Returns the path to the missing Bungie image."""
115        return "img/misc/missing_icon_d2.png"
116
117    def create_url(self) -> str:
118        """Creates a full URL to the image path.
119
120        Returns
121        -------
122        str
123            The URL to the image.
124        """
125        return f"{url.BASE}/{self._path if self._path else self.missing_path()}"
126
127    async def save(
128        self,
129        file_name: str,
130        path: typing.Union[pathlib.Path, str],
131        /,
132        mime_type: typing.Optional[typing.Union[MimeType, str]] = None,
133    ) -> None:
134        """Saves the image to a file.
135
136        Parameters
137        ----------
138        file_name : `str`
139            A name for the file to save the image to.
140        path : `pathlib.Path | str`
141            A path tp save the image to.
142
143        Other Parameters
144        ----------------
145        mime_type : `MimeType | str`
146            Optional MIME type of the image.
147
148        Raises
149        ------
150        `FileNotFoundError`
151            If the path provided does not exist.
152        `RuntimeError`
153            If the image could not be saved.
154        `PermissionError`
155            If the path provided is not writable or does not have write permissions.
156        """
157        if isinstance(path, pathlib.Path) and not path.exists():
158            raise FileNotFoundError(f"File does not exist: {path!r}")
159
160        if self.is_missing:
161            return
162
163        mimetype = mime_type or MimeType.PNG
164        path = pathlib.Path(path)
165
166        loop = helpers.get_or_make_loop()
167        pool = concurrent.futures.ThreadPoolExecutor()
168
169        try:
170            with pool:
171                await loop.run_in_executor(
172                    pool, _write, path, file_name, mimetype, await self.read()
173                )
174                _LOGGER.info("Saved image to %s", file_name)
175
176        except asyncio.CancelledError:
177            pass
178
179        except Exception as err:
180            raise RuntimeError("Encountered an error while saving image.") from err
181
182    async def read(self) -> bytes:
183        """Read this image bytes.
184
185        Returns
186        -------
187        `bytes`
188            The bytes of this image.
189        """
190        client_session = aiohttp.ClientSession()
191
192        try:
193            await client_session.__aenter__()
194            response = await client_session.get(self.create_url())
195
196            if 300 >= response.status >= 200:
197                reader = await response.read()
198
199        except Exception as exc:
200            raise RuntimeError(f"Failed to read image: {exc}") from None
201        finally:
202            await client_session.__aexit__(None, None, None)
203        return reader
204
205    async def iter(self) -> collections.AsyncGenerator[bytes, None]:
206        """Iterates over the image bytes lazily.
207
208        Example
209        -------
210        import aiobungie
211
212        resource = aiobungie.Image("img/misc/missing_icon_d2.png")
213        async for chunk in resource.iter():
214            print(chunk)
215
216        Returns
217        -------
218        `collections.AsyncGenerator[bytes, None]`
219            An async generator of the image bytes.
220        """
221
222        async for chunk in self:
223            yield chunk
224
225    def __repr__(self) -> str:
226        return f"Image(url={self.create_url()})"
227
228    def __str__(self) -> str:
229        return self.create_url()
230
231    def __aiter__(self) -> Image:
232        return self
233
234    async def __anext__(self) -> bytes:
235        return await self.read()
236
237    def __await__(self) -> collections.Generator[None, None, bytes]:
238        return self.__anext__().__await__()

Representation of an image/avatar/picture at Bungie.

Example
from aiobungie import Image
img = Image("img/destiny_content/pgcr/raid_eclipse.jpg")
print(img)
# https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg

# Stream the image.
async for chunk in img:
    # Byte chunks of the image.
    print(chunk)

# Save the image to a file.
await img.save("file_name", "/my/path/to/save/to", "jpeg")
Parameters
  • path (str | None): The path to the image. If None, the default missing image path will be used.
Image(path: Optional[str] = None)
100    def __init__(self, path: typing.Optional[str] = None) -> None:
101        self._path = path
is_missing: bool
url: str

The URL to the image.

@staticmethod
def missing_path() -> str:
112    @staticmethod
113    def missing_path() -> str:
114        """Returns the path to the missing Bungie image."""
115        return "img/misc/missing_icon_d2.png"

Returns the path to the missing Bungie image.

def create_url(self) -> str:
117    def create_url(self) -> str:
118        """Creates a full URL to the image path.
119
120        Returns
121        -------
122        str
123            The URL to the image.
124        """
125        return f"{url.BASE}/{self._path if self._path else self.missing_path()}"

Creates a full URL to the image path.

Returns
  • str: The URL to the image.
async def save( self, file_name: str, path: Union[pathlib.Path, str], /, mime_type: Union[aiobungie.internal.assets.MimeType, str, NoneType] = None) -> None:
127    async def save(
128        self,
129        file_name: str,
130        path: typing.Union[pathlib.Path, str],
131        /,
132        mime_type: typing.Optional[typing.Union[MimeType, str]] = None,
133    ) -> None:
134        """Saves the image to a file.
135
136        Parameters
137        ----------
138        file_name : `str`
139            A name for the file to save the image to.
140        path : `pathlib.Path | str`
141            A path tp save the image to.
142
143        Other Parameters
144        ----------------
145        mime_type : `MimeType | str`
146            Optional MIME type of the image.
147
148        Raises
149        ------
150        `FileNotFoundError`
151            If the path provided does not exist.
152        `RuntimeError`
153            If the image could not be saved.
154        `PermissionError`
155            If the path provided is not writable or does not have write permissions.
156        """
157        if isinstance(path, pathlib.Path) and not path.exists():
158            raise FileNotFoundError(f"File does not exist: {path!r}")
159
160        if self.is_missing:
161            return
162
163        mimetype = mime_type or MimeType.PNG
164        path = pathlib.Path(path)
165
166        loop = helpers.get_or_make_loop()
167        pool = concurrent.futures.ThreadPoolExecutor()
168
169        try:
170            with pool:
171                await loop.run_in_executor(
172                    pool, _write, path, file_name, mimetype, await self.read()
173                )
174                _LOGGER.info("Saved image to %s", file_name)
175
176        except asyncio.CancelledError:
177            pass
178
179        except Exception as err:
180            raise RuntimeError("Encountered an error while saving image.") from err

Saves the image to a file.

Parameters
  • file_name (str): A name for the file to save the image to.
  • path (pathlib.Path | str): A path tp save the image to.
Other Parameters
  • mime_type (MimeType | str): Optional MIME type of the image.
Raises
  • FileNotFoundError: If the path provided does not exist.
  • RuntimeError: If the image could not be saved.
  • PermissionError: If the path provided is not writable or does not have write permissions.
async def read(self) -> bytes:
182    async def read(self) -> bytes:
183        """Read this image bytes.
184
185        Returns
186        -------
187        `bytes`
188            The bytes of this image.
189        """
190        client_session = aiohttp.ClientSession()
191
192        try:
193            await client_session.__aenter__()
194            response = await client_session.get(self.create_url())
195
196            if 300 >= response.status >= 200:
197                reader = await response.read()
198
199        except Exception as exc:
200            raise RuntimeError(f"Failed to read image: {exc}") from None
201        finally:
202            await client_session.__aexit__(None, None, None)
203        return reader

Read this image bytes.

Returns
  • bytes: The bytes of this image.
async def iter(self) -> collections.abc.AsyncGenerator[bytes, None]:
205    async def iter(self) -> collections.AsyncGenerator[bytes, None]:
206        """Iterates over the image bytes lazily.
207
208        Example
209        -------
210        import aiobungie
211
212        resource = aiobungie.Image("img/misc/missing_icon_d2.png")
213        async for chunk in resource.iter():
214            print(chunk)
215
216        Returns
217        -------
218        `collections.AsyncGenerator[bytes, None]`
219            An async generator of the image bytes.
220        """
221
222        async for chunk in self:
223            yield chunk

Iterates over the image bytes lazily.

Example

import aiobungie

resource = aiobungie.Image("img/misc/missing_icon_d2.png") async for chunk in resource.iter(): print(chunk)

Returns
  • collections.AsyncGenerator[bytes, None]: An async generator of the image bytes.
@attrs.define(auto_exc=True)
class InternalServerError(aiobungie.HTTPException):
190@attrs.define(auto_exc=True)
191class InternalServerError(HTTPException):
192    """Raised for 5xx internal server errors."""

Raised for 5xx internal server errors.

InternalServerError( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class InternalServerError.

Inherited Members
HTTPException
error_code
http_status
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class ItemBindStatus(builtins.int, aiobungie.Enum):
722@typing.final
723class ItemBindStatus(int, Enum):
724    """An enum for Destiny 2 items bind status."""
725
726    NOT_BOUND = 0
727    BOUND_TO_CHARACTER = 1
728    BOUND_TO_ACCOUNT = 2
729    BOUNT_TO_GUILD = 3

An enum for Destiny 2 items bind status.

NOT_BOUND = <ItemBindStatus.NOT_BOUND: 0>
BOUND_TO_CHARACTER = <ItemBindStatus.BOUND_TO_CHARACTER: 1>
BOUND_TO_ACCOUNT = <ItemBindStatus.BOUND_TO_ACCOUNT: 2>
BOUNT_TO_GUILD = <ItemBindStatus.BOUNT_TO_GUILD: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemLocation(builtins.int, aiobungie.Enum):
732@typing.final
733class ItemLocation(int, Enum):
734    """An enum for Destiny 2 items location."""
735
736    UNKNOWN = 0
737    INVENTORY = 1
738    VAULT = 2
739    VENDOR = 3
740    POSTMASTER = 4

An enum for Destiny 2 items location.

UNKNOWN = <ItemLocation.UNKNOWN: 0>
INVENTORY = <ItemLocation.INVENTORY: 1>
VAULT = <ItemLocation.VAULT: 2>
VENDOR = <ItemLocation.VENDOR: 3>
POSTMASTER = <ItemLocation.POSTMASTER: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemState(aiobungie.Flag):
757@typing.final
758class ItemState(Flag):
759    """An enum for Destiny 2 item states."""
760
761    NONE = 0
762    LOCKED = 1
763    TRACKED = 2
764    MASTERWORKED = 4
765    CRAFTED = 8
766    """If this bit is set, the item has been 'crafted' by the player."""
767    HIGHLITED_OBJECTIVE = 16
768    """If this bit is set, the item is a 'highlighted' objective."""

An enum for Destiny 2 item states.

NONE = <ItemState.NONE: 0>
LOCKED = <ItemState.LOCKED: 1>
TRACKED = <ItemState.TRACKED: 2>
MASTERWORKED = <ItemState.MASTERWORKED: 4>
CRAFTED = <ItemState.CRAFTED: 8>

If this bit is set, the item has been 'crafted' by the player.

HIGHLITED_OBJECTIVE = <ItemState.HIGHLITED_OBJECTIVE: 16>

If this bit is set, the item is a 'highlighted' objective.

Inherited Members
Flag
name
value
@typing.final
class ItemSubType(builtins.int, aiobungie.Enum):
589@typing.final
590class ItemSubType(int, Enum):
591    """An enum for Destiny 2 inventory items subtype."""
592
593    NONE = 0
594    AUTORIFLE = 6
595    SHOTGUN = 7
596    MACHINEGUN = 8
597    HANDCANNON = 9
598    ROCKETLAUNCHER = 10
599    FUSIONRIFLE = 11
600    SNIPERRIFLE = 12
601    PULSERIFLE = 13
602    SCOUTRIFLE = 14
603    SIDEARM = 17
604    SWORD = 18
605    MASK = 19
606    SHADER = 20
607    ORNAMENT = 21
608    FUSIONRIFLELINE = 22
609    GRENADELAUNCHER = 23
610    SUBMACHINEGUN = 24
611    TRACERIFLE = 25
612    HELMETARMOR = 26
613    GAUNTLETSARMOR = 27
614    CHESTARMOR = 28
615    LEGARMOR = 29
616    CLASSARMOR = 30
617    BOW = 31
618    DUMMYREPEATABLEBOUNTY = 32

An enum for Destiny 2 inventory items subtype.

NONE = <ItemSubType.NONE: 0>
AUTORIFLE = <ItemSubType.AUTORIFLE: 6>
SHOTGUN = <ItemSubType.SHOTGUN: 7>
MACHINEGUN = <ItemSubType.MACHINEGUN: 8>
HANDCANNON = <ItemSubType.HANDCANNON: 9>
ROCKETLAUNCHER = <ItemSubType.ROCKETLAUNCHER: 10>
FUSIONRIFLE = <ItemSubType.FUSIONRIFLE: 11>
SNIPERRIFLE = <ItemSubType.SNIPERRIFLE: 12>
PULSERIFLE = <ItemSubType.PULSERIFLE: 13>
SCOUTRIFLE = <ItemSubType.SCOUTRIFLE: 14>
SIDEARM = <ItemSubType.SIDEARM: 17>
SWORD = <ItemSubType.SWORD: 18>
MASK = <ItemSubType.MASK: 19>
SHADER = <ItemSubType.SHADER: 20>
ORNAMENT = <ItemSubType.ORNAMENT: 21>
FUSIONRIFLELINE = <ItemSubType.FUSIONRIFLELINE: 22>
GRENADELAUNCHER = <ItemSubType.GRENADELAUNCHER: 23>
SUBMACHINEGUN = <ItemSubType.SUBMACHINEGUN: 24>
TRACERIFLE = <ItemSubType.TRACERIFLE: 25>
HELMETARMOR = <ItemSubType.HELMETARMOR: 26>
GAUNTLETSARMOR = <ItemSubType.GAUNTLETSARMOR: 27>
CHESTARMOR = <ItemSubType.CHESTARMOR: 28>
LEGARMOR = <ItemSubType.LEGARMOR: 29>
CLASSARMOR = <ItemSubType.CLASSARMOR: 30>
BOW = <ItemSubType.BOW: 31>
DUMMYREPEATABLEBOUNTY = <ItemSubType.DUMMYREPEATABLEBOUNTY: 32>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemTier(builtins.int, aiobungie.Enum):
621@typing.final
622class ItemTier(int, Enum):
623    """An enum for a Destiny 2 item tier."""
624
625    NONE = 0
626    BASIC = 3340296461
627    COMMON = 2395677314
628    RARE = 2127292149
629    LEGENDERY = 4008398120
630    EXOTIC = 2759499571

An enum for a Destiny 2 item tier.

NONE = <ItemTier.NONE: 0>
BASIC = <ItemTier.BASIC: 3340296461>
COMMON = <ItemTier.COMMON: 2395677314>
RARE = <ItemTier.RARE: 2127292149>
LEGENDERY = <ItemTier.LEGENDERY: 4008398120>
EXOTIC = <ItemTier.EXOTIC: 2759499571>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemType(builtins.int, aiobungie.Enum):
556@typing.final
557class ItemType(int, Enum):
558    """Enums for Destiny2's item types."""
559
560    NONE = 0
561    CURRENCY = 1
562    ARMOR = 2
563    WEAPON = 3
564    MESSAGE = 7
565    ENGRAM = 8
566    CONSUMABLE = 9
567    EXCHANGEMATERIAL = 10
568    MISSIONREWARD = 11
569    QUESTSTEP = 12
570    QUESTSTEPCOMPLETE = 13
571    EMBLEM = 14
572    QUEST = 15
573    SUBCLASS = 16
574    CLANBANNER = 17
575    AURA = 18
576    MOD = 19
577    DUMMY = 20
578    SHIP = 21
579    VEHICLE = 22
580    EMOTE = 23
581    GHOST = 24
582    PACKAGE = 25
583    BOUNTY = 26
584    WRAPPER = 27
585    SEASONALARTIFACT = 28
586    FINISHER = 29

Enums for Destiny2's item types.

NONE = <ItemType.NONE: 0>
CURRENCY = <ItemType.CURRENCY: 1>
ARMOR = <ItemType.ARMOR: 2>
WEAPON = <ItemType.WEAPON: 3>
MESSAGE = <ItemType.MESSAGE: 7>
ENGRAM = <ItemType.ENGRAM: 8>
CONSUMABLE = <ItemType.CONSUMABLE: 9>
EXCHANGEMATERIAL = <ItemType.EXCHANGEMATERIAL: 10>
MISSIONREWARD = <ItemType.MISSIONREWARD: 11>
QUESTSTEP = <ItemType.QUESTSTEP: 12>
QUESTSTEPCOMPLETE = <ItemType.QUESTSTEPCOMPLETE: 13>
EMBLEM = <ItemType.EMBLEM: 14>
QUEST = <ItemType.QUEST: 15>
SUBCLASS = <ItemType.SUBCLASS: 16>
CLANBANNER = <ItemType.CLANBANNER: 17>
AURA = <ItemType.AURA: 18>
MOD = <ItemType.MOD: 19>
DUMMY = <ItemType.DUMMY: 20>
SHIP = <ItemType.SHIP: 21>
VEHICLE = <ItemType.VEHICLE: 22>
EMOTE = <ItemType.EMOTE: 23>
GHOST = <ItemType.GHOST: 24>
PACKAGE = <ItemType.PACKAGE: 25>
BOUNTY = <ItemType.BOUNTY: 26>
WRAPPER = <ItemType.WRAPPER: 27>
SEASONALARTIFACT = <ItemType.SEASONALARTIFACT: 28>
FINISHER = <ItemType.FINISHER: 29>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class MembershipOption(builtins.int, aiobungie.Enum):
713@typing.final
714class MembershipOption(int, Enum):
715    """A enum for GroupV2 membership options."""
716
717    REVIEWD = 0
718    OPEN = 1
719    CLOSED = 2

A enum for GroupV2 membership options.

REVIEWD = <MembershipOption.REVIEWD: 0>
OPEN = <MembershipOption.OPEN: 1>
CLOSED = <MembershipOption.CLOSED: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class MembershipType(builtins.int, aiobungie.Enum):
463@typing.final
464class MembershipType(int, Enum):
465    """An Enum for Bungie membership types."""
466
467    NONE = 0
468    XBOX = 1
469    PSN = 2
470    STEAM = 3
471    BLIZZARD = 4
472    STADIA = 5
473    BUNGIE = 254
474    ALL = -1

An Enum for Bungie membership types.

NONE = <MembershipType.NONE: 0>
XBOX = <MembershipType.XBOX: 1>
PSN = <MembershipType.PSN: 2>
STEAM = <MembershipType.STEAM: 3>
BLIZZARD = <MembershipType.BLIZZARD: 4>
STADIA = <MembershipType.STADIA: 5>
BUNGIE = <MembershipType.BUNGIE: 254>
ALL = <MembershipType.ALL: -1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class MembershipTypeError(aiobungie.BadRequest):
163@attrs.define(auto_exc=True)
164class MembershipTypeError(BadRequest):
165    """A bad request error raised when passing wrong membership to the request.
166
167    Those fields are useful since it returns the correct membership and id which can be used
168    to make the request again with those fields.
169    """
170
171    membership_type: str = attrs.field(default="")
172    """The errored membership type passed to the request."""
173
174    membership_id: int = attrs.field(default=0)
175    """The errored user's membership id."""
176
177    required_membership: str = attrs.field(default="")
178    """The required correct membership for errored user."""
179
180    def __str__(self) -> str:
181        return (
182            f"Expected membership: {self.required_membership}, "
183            f"But got {self.membership_type} for id {self.membership_id}"
184        )
185
186    def __int__(self) -> int:
187        return int(self.membership_id)

A bad request error raised when passing wrong membership to the request.

Those fields are useful since it returns the correct membership and id which can be used to make the request again with those fields.

MembershipTypeError( message: str, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], http_status: http.HTTPStatus = <HTTPStatus.BAD_REQUEST: 400>, membership_type: str = '', membership_id: int = 0, required_membership: str = '')
 2def __init__(self, message, url, body, headers, http_status=attr_dict['http_status'].default, membership_type=attr_dict['membership_type'].default, membership_id=attr_dict['membership_id'].default, required_membership=attr_dict['required_membership'].default):
 3    self.message = message
 4    self.url = url
 5    self.body = body
 6    self.headers = headers
 7    self.http_status = http_status
 8    self.membership_type = membership_type
 9    self.membership_id = membership_id
10    self.required_membership = required_membership
11    BaseException.__init__(self, self.message,self.url,self.body,self.headers,self.http_status,self.membership_type,self.membership_id,self.required_membership)

Method generated by attrs for class MembershipTypeError.

membership_type: str

The errored membership type passed to the request.

membership_id: int

The errored user's membership id.

required_membership: str

The required correct membership for errored user.

Inherited Members
BadRequest
url
body
headers
http_status
HTTPError
message
builtins.BaseException
with_traceback
args
@typing.final
class MilestoneType(builtins.int, aiobungie.Enum):
506@typing.final
507class MilestoneType(int, Enum):
508    """An Enum for Destiny 2 milestone types."""
509
510    UNKNOWN = 0
511    TUTORIAL = 1
512    ONETIME = 2
513    WEEKLY = 3
514    DAILY = 4
515    SPECIAL = 5

An Enum for Destiny 2 milestone types.

UNKNOWN = <MilestoneType.UNKNOWN: 0>
TUTORIAL = <MilestoneType.TUTORIAL: 1>
ONETIME = <MilestoneType.ONETIME: 2>
WEEKLY = <MilestoneType.WEEKLY: 3>
DAILY = <MilestoneType.DAILY: 4>
SPECIAL = <MilestoneType.SPECIAL: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class NotFound(aiobungie.HTTPException):
129@attrs.define(auto_exc=True)
130class NotFound(HTTPException):
131    """Raised when an unknown request was not found."""
132
133    http_status: http.HTTPStatus = attrs.field(
134        default=http.HTTPStatus.NOT_FOUND, init=False
135    )

Raised when an unknown request was not found.

NotFound( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class NotFound.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class ObjectiveUIStyle(builtins.int, aiobungie.Enum):
 94@typing.final
 95class ObjectiveUIStyle(int, enums.Enum):
 96    NONE = 0
 97    HIGHLIGHTED = 1
 98    CRAFTING_WEAPON_LEVEL = 2
 99    CRAFTING_WEAPON_LEVEL_PROGRESS = 3
100    CRAFTING_WEAPON_TIMESTAMP = 4
101    CRAFTING_MEMENTOS = 5
102    CRAFTING_MEMENTO_TITLE = 6

An enumeration.

NONE = <ObjectiveUIStyle.NONE: 0>
HIGHLIGHTED = <ObjectiveUIStyle.HIGHLIGHTED: 1>
CRAFTING_WEAPON_LEVEL = <ObjectiveUIStyle.CRAFTING_WEAPON_LEVEL: 2>
CRAFTING_WEAPON_LEVEL_PROGRESS = <ObjectiveUIStyle.CRAFTING_WEAPON_LEVEL_PROGRESS: 3>
CRAFTING_WEAPON_TIMESTAMP = <ObjectiveUIStyle.CRAFTING_WEAPON_TIMESTAMP: 4>
CRAFTING_MEMENTOS = <ObjectiveUIStyle.CRAFTING_MEMENTOS: 5>
CRAFTING_MEMENTO_TITLE = <ObjectiveUIStyle.CRAFTING_MEMENTO_TITLE: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Place(builtins.int, aiobungie.Enum):
235@typing.final
236class Place(int, Enum):
237    """An Enum for Destiny 2 Places and NOT Planets"""
238
239    ORBIT = 2961497387
240    SOCIAL = 4151112093
241    LIGHT_HOUSE = 4276116472
242    EXPLORE = 3497767639

An Enum for Destiny 2 Places and NOT Planets

ORBIT = <Place.ORBIT: 2961497387>
SOCIAL = <Place.SOCIAL: 4151112093>
LIGHT_HOUSE = <Place.LIGHT_HOUSE: 4276116472>
EXPLORE = <Place.EXPLORE: 3497767639>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Planet(builtins.int, aiobungie.Enum):
200@typing.final
201class Planet(int, Enum):
202    """An Enum for all available planets in Destiny 2."""
203
204    UNKNOWN = 0
205    """Unknown space"""
206
207    EARTH = 3747705955
208    """Earth"""
209
210    DREAMING_CITY = 2877881518
211    """The Dreaming city."""
212
213    NESSUS = 3526908984
214    """Nessus"""
215
216    MOON = 3325508439
217    """The Moon"""
218
219    COSMODROME = 3990611421
220    """The Cosmodrome"""
221
222    TANGLED_SHORE = 3821439926
223    """The Tangled Shore"""
224
225    VENUS = 3871070152
226    """Venus"""
227
228    EAZ = 541863059  # Exclusive event.
229    """European Aerial Zone"""
230
231    EUROPA = 1729879943
232    """Europa"""

An Enum for all available planets in Destiny 2.

UNKNOWN = <Planet.UNKNOWN: 0>

Unknown space

EARTH = <Planet.EARTH: 3747705955>

Earth

DREAMING_CITY = <Planet.DREAMING_CITY: 2877881518>

The Dreaming city.

NESSUS = <Planet.NESSUS: 3526908984>

Nessus

MOON = <Planet.MOON: 3325508439>

The Moon

COSMODROME = <Planet.COSMODROME: 3990611421>

The Cosmodrome

TANGLED_SHORE = <Planet.TANGLED_SHORE: 3821439926>

The Tangled Shore

VENUS = <Planet.VENUS: 3871070152>

Venus

EAZ = <Planet.EAZ: 541863059>

European Aerial Zone

EUROPA = <Planet.EUROPA: 1729879943>

Europa

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Presence(builtins.int, aiobungie.Enum):
683@typing.final
684class Presence(int, Enum):
685    """An enum for a bungie friend status."""
686
687    OFFLINE_OR_UNKNOWN = 0
688    ONLINE = 1

An enum for a bungie friend status.

OFFLINE_OR_UNKNOWN = <Presence.OFFLINE_OR_UNKNOWN: 0>
ONLINE = <Presence.ONLINE: 1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class PrivacySetting(builtins.int, aiobungie.Enum):
771@typing.final
772class PrivacySetting(int, Enum):
773    """An enum for players's privacy settings."""
774
775    OPEN = 0
776    CLAN_AND_FRIENDS = 1
777    FRIENDS_ONLY = 2
778    INVITE_ONLY = 3
779    CLOSED = 4

An enum for players's privacy settings.

OPEN = <PrivacySetting.OPEN: 0>
CLAN_AND_FRIENDS = <PrivacySetting.CLAN_AND_FRIENDS: 1>
FRIENDS_ONLY = <PrivacySetting.FRIENDS_ONLY: 2>
INVITE_ONLY = <PrivacySetting.INVITE_ONLY: 3>
CLOSED = <PrivacySetting.CLOSED: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class RESTClient(aiobungie.interfaces.rest.RESTInterface):
 352class RESTClient(interfaces.RESTInterface):
 353    """A RESTful client implementation for Bungie's API.
 354
 355    This client is designed to only make HTTP requests and return JSON objects
 356    to provide RESTful functionality.
 357
 358    This client is also used within `aiobungie.Client` which deserialize those returned JSON objects
 359    using the factory into Pythonic data classes objects which provide Python functionality.
 360
 361    Example
 362    -------
 363    ```py
 364    import aiobungie
 365
 366    async def main():
 367        async with aiobungie.RESTClient("TOKEN") as rest_client:
 368            req = await rest_client.fetch_clan_members(4389205)
 369            clan_members = req['results']
 370            for member in clan_members:
 371                for k, v in member['destinyUserInfo'].items():
 372                    print(k, v)
 373    ```
 374
 375    Parameters
 376    ----------
 377    token : `str`
 378        A valid application token from Bungie's developer portal.
 379
 380    Other Parameters
 381    ----------------
 382    max_retries : `int`
 383        The max retries number to retry if the request hit a `5xx` status code.
 384    max_ratelimit_retries : `int`
 385        The max retries number to retry if the request hit a `429` status code. Defaults to `3`.
 386    client_secret : `typing.Optional[str]`
 387        An optional application client secret,
 388        This is only needed if you're fetching OAuth2 tokens with this client.
 389    client_id : `typing.Optional[int]`
 390        An optional application client id,
 391        This is only needed if you're fetching OAuth2 tokens with this client.
 392    enable_debugging : `bool | str`
 393        Whether to enable logging responses or not.
 394
 395    Logging Levels
 396    --------------
 397    * `False`: This will disable logging.
 398    * `True`: This will set the level to `DEBUG` and enable logging minimal information.
 399    * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
 400    """
 401
 402    __slots__ = (
 403        "_token",
 404        "_session",
 405        "_lock",
 406        "_max_retries",
 407        "_client_secret",
 408        "_client_id",
 409        "_metadata",
 410        "_max_rate_limit_retries",
 411    )
 412
 413    def __init__(
 414        self,
 415        token: str,
 416        /,
 417        client_secret: typing.Optional[str] = None,
 418        client_id: typing.Optional[int] = None,
 419        *,
 420        max_retries: int = 4,
 421        max_ratelimit_retries: int = 3,
 422        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
 423    ) -> None:
 424        self._session: typing.Optional[_Session] = None
 425        self._lock: typing.Optional[asyncio.Lock] = None
 426        self._client_secret = client_secret
 427        self._client_id = client_id
 428        self._token: str = token
 429        self._max_retries = max_retries
 430        self._max_rate_limit_retries = max_ratelimit_retries
 431        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
 432
 433        self._set_debug_level(enable_debugging)
 434
 435    @property
 436    def client_id(self) -> typing.Optional[int]:
 437        return self._client_id
 438
 439    @property
 440    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
 441        return self._metadata
 442
 443    @property
 444    def is_alive(self) -> bool:
 445        return self._session is not None
 446
 447    @typing.final
 448    async def close(self) -> None:
 449        if self._session is not None:
 450            await self._session.close()
 451            self._session = None
 452
 453    @typing.final
 454    def enable_debugging(
 455        self,
 456        level: typing.Union[typing.Literal["TRACE"], bool, int] = False,
 457        file: typing.Optional[typing.Union[pathlib.Path, str]] = None,
 458        /,
 459    ) -> None:
 460        self._set_debug_level(level, file)
 461
 462    @typing.final
 463    async def static_request(
 464        self,
 465        method: typing.Union[RequestMethod, str],
 466        path: str,
 467        *,
 468        auth: typing.Optional[str] = None,
 469        json: typing.Optional[dict[str, typing.Any]] = None,
 470    ) -> ResponseSig:
 471        return await self._request(method, path, auth=auth, json=json)
 472
 473    @typing.final
 474    def build_oauth2_url(
 475        self, client_id: typing.Optional[int] = None
 476    ) -> typing.Optional[str]:
 477        client_id = client_id or self._client_id
 478        if client_id is None:
 479            return None
 480
 481        return url.OAUTH2_EP_BUILDER.format(
 482            oauth_endpoint=url.OAUTH_EP,
 483            client_id=client_id,
 484            uuid=_uuid(),
 485        )
 486
 487    def _open(self) -> _Session:
 488        """Open a new client session. This is called internally with contextmanager usage."""
 489        asyncio.get_running_loop()
 490        if self._session is None:
 491            self._session = _Session.create(
 492                owner=False,
 493                raise_status=False,
 494                connect=None,
 495                socket_read=None,
 496                socket_connect=None,
 497            )
 498        return self._session
 499
 500    @staticmethod
 501    def _set_debug_level(
 502        level: typing.Union[typing.Literal["TRACE"], bool, int] = False,
 503        file: typing.Optional[typing.Union[pathlib.Path, str]] = None,
 504    ) -> None:
 505
 506        file_handler = logging.FileHandler(file, mode="w") if file else None
 507        if level == "TRACE" or level == TRACE:
 508            logging.basicConfig(
 509                level=TRACE, handlers=[file_handler] if file_handler else None
 510            )
 511
 512        elif level:
 513            logging.basicConfig(
 514                level=logging.DEBUG, handlers=[file_handler] if file_handler else None
 515            )
 516
 517    async def _request(
 518        self,
 519        method: typing.Union[RequestMethod, str],
 520        route: str,
 521        *,
 522        base: bool = False,
 523        oauth2: bool = False,
 524        auth: typing.Optional[str] = None,
 525        unwrapping: typing.Literal["json", "read"] = "json",
 526        json: typing.Optional[dict[str, typing.Any]] = None,
 527        headers: typing.Optional[dict[str, typing.Any]] = None,
 528        data: typing.Optional[typing.Union[str, dict[str, typing.Any]]] = None,
 529    ) -> ResponseSig:
 530
 531        retries: int = 0
 532        session = self._open()
 533        headers = headers or {}
 534
 535        headers.setdefault(_USER_AGENT_HEADERS, _USER_AGENT)
 536        headers["X-API-KEY"] = self._token
 537
 538        if auth is not None:
 539            headers[_AUTH_HEADER] = f"Bearer {auth}"
 540
 541        # Handling endpoints
 542        endpoint = url.BASE
 543
 544        if not base:
 545            endpoint = endpoint + url.REST_EP
 546
 547        if oauth2:
 548            headers["Content-Type"] = "application/x-www-form-urlencoded"
 549            endpoint = endpoint + url.TOKEN_EP
 550
 551        if self._lock is None:
 552            self._lock = asyncio.Lock()
 553
 554        while True:
 555            try:
 556                async with (stack := contextlib.AsyncExitStack()):
 557                    await stack.enter_async_context(self._lock)
 558
 559                    # We make the request here.
 560                    taken_time = time.monotonic()
 561                    response = await stack.enter_async_context(
 562                        session.client_session.request(
 563                            method=method,
 564                            url=f"{endpoint}/{route}",
 565                            json=json,
 566                            headers=headers,
 567                            data=data,
 568                        )
 569                    )
 570                    response_time = (time.monotonic() - taken_time) * 1_000
 571
 572                    _LOG.debug(
 573                        "%s %s %s Time %.4fms",
 574                        method,
 575                        f"{endpoint}/{route}",
 576                        f"{response.status} {response.reason}",
 577                        response_time,
 578                    )
 579
 580                    await self._handle_ratelimit(
 581                        response, method, route, self._max_rate_limit_retries
 582                    )
 583
 584                    if response.status == http.HTTPStatus.NO_CONTENT:
 585                        return None
 586
 587                    if 300 > response.status >= 200:
 588                        if unwrapping == "read":
 589                            # We need to read the bytes for the manifest response.
 590                            return await response.read()
 591
 592                        if response.content_type == _APP_JSON:
 593                            json_data = await response.json()
 594
 595                            _LOG.debug(
 596                                "%s %s %s Time %.4fms",
 597                                method,
 598                                f"{endpoint}/{route}",
 599                                f"{response.status} {response.reason}",
 600                                response_time,
 601                            )
 602
 603                            if _LOG.isEnabledFor(TRACE):
 604                                headers.update(response.headers)
 605
 606                                _LOG.log(
 607                                    TRACE,
 608                                    "%s",
 609                                    error.stringify_http_message(headers),
 610                                )
 611
 612                            # Return the response.
 613                            # oauth2 responses are not packed inside a Response object.
 614                            if oauth2:
 615                                return json_data  # type: ignore[no-any-return]
 616
 617                            return json_data["Response"]  # type: ignore[no-any-return]
 618
 619                    if (
 620                        response.status in _RETRY_5XX
 621                        and retries < self._max_retries  # noqa: W503
 622                    ):
 623                        backoff_ = backoff.ExponentialBackOff(maximum=6)
 624                        sleep_time = next(backoff_)
 625                        _LOG.warning(
 626                            "Got %i - %s. Sleeping for %.2f seconds. Remaining retries: %i",
 627                            response.status,
 628                            response.reason,
 629                            sleep_time,
 630                            self._max_retries - retries,
 631                        )
 632
 633                        retries += 1
 634                        await asyncio.sleep(sleep_time)
 635                        continue
 636
 637                    raise await error.raise_error(response)
 638            # eol
 639            except _Dyn:
 640                continue
 641
 642    if not typing.TYPE_CHECKING:
 643
 644        def __enter__(self) -> typing.NoReturn:
 645            cls = type(self)
 646            raise TypeError(
 647                f"{cls.__qualname__} is async only, use 'async with' instead."
 648            )
 649
 650        def __exit__(
 651            self,
 652            exception_type: typing.Optional[type[BaseException]],
 653            exception: typing.Optional[BaseException],
 654            exception_traceback: typing.Optional[types.TracebackType],
 655        ) -> None:
 656            ...
 657
 658    async def __aenter__(self) -> RESTClient:
 659        self._open()
 660        return self
 661
 662    async def __aexit__(
 663        self,
 664        exception_type: typing.Optional[type[BaseException]],
 665        exception: typing.Optional[BaseException],
 666        exception_traceback: typing.Optional[types.TracebackType],
 667    ) -> None:
 668        await self.close()
 669
 670    # We don't want this to be super complicated.
 671    @staticmethod
 672    @typing.final
 673    async def _handle_ratelimit(
 674        response: aiohttp.ClientResponse,
 675        method: str,
 676        route: str,
 677        max_ratelimit_retries: int = 3,
 678    ) -> None:
 679
 680        if response.status != http.HTTPStatus.TOO_MANY_REQUESTS:
 681            return
 682
 683        if response.content_type != _APP_JSON:
 684            raise error.HTTPError(
 685                f"Being ratelimited on non JSON request, {response.content_type}.",
 686                http.HTTPStatus.TOO_MANY_REQUESTS,
 687            )
 688
 689        count: int = 0
 690        json: typedefs.JSONObject = await response.json()
 691        retry_after = float(json["ThrottleSeconds"])
 692
 693        while True:
 694            if count == max_ratelimit_retries:
 695                raise _Dyn
 696
 697            if retry_after <= 0:
 698                # We sleep for a little bit to avoid funky behavior.
 699                sleep_time = float(random.random() + 0.93) / 2
 700
 701                _LOG.warning(
 702                    "We're being ratelimited with method %s route %s. Sleeping for %.2fs.",
 703                    method,
 704                    route,
 705                    sleep_time,
 706                )
 707                count += 1
 708                await asyncio.sleep(sleep_time)
 709                continue
 710
 711            raise error.RateLimitedError(
 712                body=json,
 713                url=str(response.real_url),
 714                retry_after=retry_after,
 715            )
 716
 717    async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response:
 718
 719        if not isinstance(self._client_id, int):
 720            raise TypeError(
 721                f"Expected (int) for client id but got {type(self._client_id).__qualname__}"  # type: ignore
 722            )
 723
 724        if not isinstance(self._client_secret, str):
 725            raise TypeError(
 726                f"Expected (str) for client secret but got {type(self._client_secret).__qualname__}"  # type: ignore
 727            )
 728
 729        headers = {
 730            "client_secret": self._client_secret,
 731        }
 732
 733        data = (
 734            f"grant_type=authorization_code&code={code}"
 735            f"&client_id={self._client_id}&client_secret={self._client_secret}"
 736        )
 737
 738        response = await self._request(
 739            RequestMethod.POST, "", headers=headers, data=data, oauth2=True
 740        )
 741        assert isinstance(response, dict)
 742        return builders.OAuth2Response.build_response(response)
 743
 744    async def refresh_access_token(
 745        self, refresh_token: str, /
 746    ) -> builders.OAuth2Response:
 747        if not isinstance(self._client_id, int):
 748            raise TypeError(
 749                f"Expected (int) for client id but got {type(self._client_id).__qualname__}"  # type: ignore
 750            )
 751
 752        if not isinstance(self._client_secret, str):
 753            raise TypeError(
 754                f"Expected (str) for client secret but got {type(self._client_secret).__qualname__}"  # type: ignore
 755            )
 756
 757        data = {
 758            "grant_type": "refresh_token",
 759            "refresh_token": refresh_token,
 760            "client_id": self._client_id,
 761            "client_secret": self._client_secret,
 762            "Content-Type": "application/x-www-form-urlencoded",
 763        }
 764
 765        response = await self._request(RequestMethod.POST, "", data=data, oauth2=True)
 766        assert isinstance(response, dict)
 767        return builders.OAuth2Response.build_response(response)
 768
 769    async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject:
 770        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 771        resp = await self._request(
 772            RequestMethod.GET, f"User/GetBungieNetUserById/{id}/"
 773        )
 774        assert isinstance(resp, dict)
 775        return resp
 776
 777    async def fetch_user_themes(self) -> typedefs.JSONArray:
 778        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 779        resp = await self._request(RequestMethod.GET, "User/GetAvailableThemes/")
 780        assert isinstance(resp, list)
 781        return resp
 782
 783    async def fetch_membership_from_id(
 784        self,
 785        id: int,
 786        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 787        /,
 788    ) -> typedefs.JSONObject:
 789        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 790        resp = await self._request(
 791            RequestMethod.GET, f"User/GetMembershipsById/{id}/{int(type)}"
 792        )
 793        assert isinstance(resp, dict)
 794        return resp
 795
 796    async def fetch_player(
 797        self,
 798        name: str,
 799        code: int,
 800        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
 801        /,
 802    ) -> typedefs.JSONArray:
 803        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 804        resp = await self._request(
 805            RequestMethod.POST,
 806            f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}",
 807            json={"displayName": name, "displayNameCode": code},
 808        )
 809        assert isinstance(resp, list)
 810        return resp
 811
 812    async def search_users(self, name: str, /) -> typedefs.JSONObject:
 813        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 814        resp = await self._request(
 815            RequestMethod.POST,
 816            "User/Search/GlobalName/0",
 817            json={"displayNamePrefix": name},
 818        )
 819        assert isinstance(resp, dict)
 820        return resp
 821
 822    async def fetch_clan_from_id(
 823        self, id: int, /, access_token: typing.Optional[str] = None
 824    ) -> typedefs.JSONObject:
 825        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 826        resp = await self._request(
 827            RequestMethod.GET, f"GroupV2/{id}", auth=access_token
 828        )
 829        assert isinstance(resp, dict)
 830        return resp
 831
 832    async def fetch_clan(
 833        self,
 834        name: str,
 835        /,
 836        access_token: typing.Optional[str] = None,
 837        *,
 838        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 839    ) -> typedefs.JSONObject:
 840        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 841        resp = await self._request(
 842            RequestMethod.GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token
 843        )
 844        assert isinstance(resp, dict)
 845        return resp
 846
 847    async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject:
 848        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 849        resp = await self._request(
 850            RequestMethod.GET, f"GroupV2/{clan_id}/AdminsAndFounder/"
 851        )
 852        assert isinstance(resp, dict)
 853        return resp
 854
 855    async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray:
 856        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 857        resp = await self._request(
 858            RequestMethod.GET, f"GroupV2/{clan_id}/OptionalConversations/"
 859        )
 860        assert isinstance(resp, list)
 861        return resp
 862
 863    async def fetch_application(self, appid: int, /) -> typedefs.JSONObject:
 864        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 865        resp = await self._request(RequestMethod.GET, f"App/Application/{appid}")
 866        assert isinstance(resp, dict)
 867        return resp
 868
 869    async def fetch_character(
 870        self,
 871        member_id: int,
 872        membership_type: typedefs.IntAnd[enums.MembershipType],
 873        character_id: int,
 874        components: list[enums.ComponentType],
 875        auth: typing.Optional[str] = None,
 876    ) -> typedefs.JSONObject:
 877        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 878        collector = _collect_components(components)
 879        response = await self._request(
 880            RequestMethod.GET,
 881            f"Destiny2/{int(membership_type)}/Profile/{member_id}/"
 882            f"Character/{character_id}/?components={collector}",
 883            auth=auth,
 884        )
 885        assert isinstance(response, dict)
 886        return response
 887
 888    async def fetch_activities(
 889        self,
 890        member_id: int,
 891        character_id: int,
 892        mode: typedefs.IntAnd[enums.GameMode],
 893        membership_type: typedefs.IntAnd[
 894            enums.MembershipType
 895        ] = enums.MembershipType.ALL,
 896        *,
 897        page: int = 0,
 898        limit: int = 1,
 899    ) -> typedefs.JSONObject:
 900        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 901        resp = await self._request(
 902            RequestMethod.GET,
 903            f"Destiny2/{int(membership_type)}/Account/"
 904            f"{member_id}/Character/{character_id}/Stats/Activities"
 905            f"/?mode={int(mode)}&count={limit}&page={page}",
 906        )
 907        assert isinstance(resp, dict)
 908        return resp
 909
 910    async def fetch_vendor_sales(self) -> typedefs.JSONObject:
 911        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 912        resp = await self._request(
 913            RequestMethod.GET,
 914            f"Destiny2/Vendors/?components={int(enums.ComponentType.VENDOR_SALES)}",
 915        )
 916        assert isinstance(resp, dict)
 917        return resp
 918
 919    async def fetch_profile(
 920        self,
 921        membership_id: int,
 922        type: typedefs.IntAnd[enums.MembershipType],
 923        components: list[enums.ComponentType],
 924        auth: typing.Optional[str] = None,
 925    ) -> typedefs.JSONObject:
 926        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 927        collector = _collect_components(components)
 928        response = await self._request(
 929            RequestMethod.GET,
 930            f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}",
 931            auth=auth,
 932        )
 933        assert isinstance(response, dict)
 934        return response
 935
 936    async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject:
 937        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 938        response = await self._request(
 939            RequestMethod.GET, route=f"Destiny2/Manifest/{type}/{hash}"
 940        )
 941        assert isinstance(response, dict)
 942        return response
 943
 944    async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject:
 945        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 946        resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash)
 947        assert isinstance(resp, dict)
 948        return resp
 949
 950    async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject:
 951        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 952        resp = await self.fetch_entity("DestinyObjectiveDefinition", hash)
 953        assert isinstance(resp, dict)
 954        return resp
 955
 956    async def fetch_groups_for_member(
 957        self,
 958        member_id: int,
 959        member_type: typedefs.IntAnd[enums.MembershipType],
 960        /,
 961        *,
 962        filter: int = 0,
 963        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 964    ) -> typedefs.JSONObject:
 965        resp = await self._request(
 966            RequestMethod.GET,
 967            f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
 968        )
 969        assert isinstance(resp, dict)
 970        return resp
 971
 972    async def fetch_potential_groups_for_member(
 973        self,
 974        member_id: int,
 975        member_type: typedefs.IntAnd[enums.MembershipType],
 976        /,
 977        *,
 978        filter: int = 0,
 979        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 980    ) -> typedefs.JSONObject:
 981        resp = await self._request(
 982            RequestMethod.GET,
 983            f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
 984        )
 985        assert isinstance(resp, dict)
 986        return resp
 987
 988    async def fetch_clan_members(
 989        self,
 990        clan_id: int,
 991        /,
 992        *,
 993        name: typing.Optional[str] = None,
 994        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 995    ) -> typedefs.JSONObject:
 996        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 997        resp = await self._request(
 998            RequestMethod.GET,
 999            f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}&currentpage=1",
1000        )
1001        assert isinstance(resp, dict)
1002        return resp
1003
1004    async def fetch_hardlinked_credentials(
1005        self,
1006        credential: int,
1007        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
1008        /,
1009    ) -> typedefs.JSONObject:
1010        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1011        resp = await self._request(
1012            RequestMethod.GET,
1013            f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/",
1014        )
1015        assert isinstance(resp, dict)
1016        return resp
1017
1018    async def fetch_user_credentials(
1019        self, access_token: str, membership_id: int, /
1020    ) -> typedefs.JSONArray:
1021        resp = await self._request(
1022            RequestMethod.GET,
1023            f"User/GetCredentialTypesForTargetAccount/{membership_id}",
1024            auth=access_token,
1025        )
1026        assert isinstance(resp, list)
1027        return resp
1028
1029    async def insert_socket_plug(
1030        self,
1031        action_token: str,
1032        /,
1033        instance_id: int,
1034        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1035        character_id: int,
1036        membership_type: typedefs.IntAnd[enums.MembershipType],
1037    ) -> typedefs.JSONObject:
1038
1039        if isinstance(plug, builders.PlugSocketBuilder):
1040            plug = plug.collect()
1041
1042        body = {
1043            "actionToken": action_token,
1044            "itemInstanceId": instance_id,
1045            "plug": plug,
1046            "characterId": character_id,
1047            "membershipType": int(membership_type),
1048        }
1049        resp = await self._request(
1050            RequestMethod.POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body
1051        )
1052        assert isinstance(resp, dict)
1053        return resp
1054
1055    async def insert_socket_plug_free(
1056        self,
1057        access_token: str,
1058        /,
1059        instance_id: int,
1060        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1061        character_id: int,
1062        membership_type: typedefs.IntAnd[enums.MembershipType],
1063    ) -> typedefs.JSONObject:
1064
1065        if isinstance(plug, builders.PlugSocketBuilder):
1066            plug = plug.collect()
1067
1068        body = {
1069            "itemInstanceId": instance_id,
1070            "plug": plug,
1071            "characterId": character_id,
1072            "membershipType": int(membership_type),
1073        }
1074        resp = await self._request(
1075            RequestMethod.POST,
1076            "Destiny2/Actions/Items/InsertSocketPlugFree",
1077            json=body,
1078            auth=access_token,
1079        )
1080        assert isinstance(resp, dict)
1081        return resp
1082
1083    async def set_item_lock_state(
1084        self,
1085        access_token: str,
1086        state: bool,
1087        /,
1088        item_id: int,
1089        character_id: int,
1090        membership_type: typedefs.IntAnd[enums.MembershipType],
1091    ) -> int:
1092        body = {
1093            "state": state,
1094            "itemId": item_id,
1095            "characterId": character_id,
1096            "membership_type": int(membership_type),
1097        }
1098        response = await self._request(
1099            RequestMethod.POST,
1100            "Destiny2/Actions/Items/SetLockState",
1101            json=body,
1102            auth=access_token,
1103        )
1104        assert isinstance(response, int)
1105        return response
1106
1107    async def set_quest_track_state(
1108        self,
1109        access_token: str,
1110        state: bool,
1111        /,
1112        item_id: int,
1113        character_id: int,
1114        membership_type: typedefs.IntAnd[enums.MembershipType],
1115    ) -> int:
1116        body = {
1117            "state": state,
1118            "itemId": item_id,
1119            "characterId": character_id,
1120            "membership_type": int(membership_type),
1121        }
1122        response = await self._request(
1123            RequestMethod.POST,
1124            "Destiny2/Actions/Items/SetTrackedState",
1125            json=body,
1126            auth=access_token,
1127        )
1128        assert isinstance(response, int)
1129        return response
1130
1131    async def fetch_manifest_path(self) -> typedefs.JSONObject:
1132        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1133        path = await self._request(RequestMethod.GET, "Destiny2/Manifest")
1134        assert isinstance(path, dict)
1135        return path
1136
1137    async def read_manifest_bytes(self, language: str = "en", /) -> bytes:
1138        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1139        _ensure_manifest_language(language)
1140
1141        content = await self.fetch_manifest_path()
1142        resp = await self._request(
1143            RequestMethod.GET,
1144            content["mobileWorldContentPaths"][language],
1145            unwrapping="read",
1146            base=True,
1147        )
1148        assert isinstance(resp, bytes)
1149        return resp
1150
1151    async def download_manifest(
1152        self,
1153        language: str = "en",
1154        name: str = "manifest.sqlite3",
1155        *,
1156        force: bool = False,
1157    ) -> None:
1158        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1159        if os.path.exists(name):
1160
1161            if force:
1162                _LOG.debug("Forcing manifest download.")
1163                os.remove(name)
1164
1165                return await self.download_manifest(language, name, force=force)
1166
1167            else:
1168                raise FileExistsError(
1169                    "Manifest file already exists, "
1170                    "If you want to force download, set the `force` parameter to `True`."
1171                )
1172
1173        _LOG.debug("Downloading manifest...")
1174        data_bytes = await self.read_manifest_bytes(language)
1175        await asyncio.get_running_loop().run_in_executor(
1176            None, _write_sqlite_bytes, data_bytes, name
1177        )
1178
1179    async def download_json_manifest(self, language: str = "en") -> None:
1180        _ensure_manifest_language(language)
1181
1182        _LOG.debug("Downloading manifest JSON...")
1183
1184        content = await self.fetch_manifest_path()
1185        json_bytes = await self._request(
1186            RequestMethod.GET,
1187            content["jsonWorldContentPaths"][language],
1188            unwrapping="read",
1189            base=True,
1190        )
1191
1192        await asyncio.get_running_loop().run_in_executor(
1193            None, _write_json_bytes, json_bytes
1194        )
1195        _LOG.debug("Finished downloading manifest JSON.")
1196
1197    async def fetch_manifest_version(self) -> str:
1198        return typing.cast(str, (await self.fetch_manifest_path())["version"])
1199
1200    async def fetch_linked_profiles(
1201        self,
1202        member_id: int,
1203        member_type: typedefs.IntAnd[enums.MembershipType],
1204        /,
1205        *,
1206        all: bool = False,
1207    ) -> typedefs.JSONObject:
1208        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1209        resp = await self._request(
1210            RequestMethod.GET,
1211            f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}",
1212        )
1213        assert isinstance(resp, dict)
1214        return resp
1215
1216    async def fetch_clan_banners(self) -> typedefs.JSONObject:
1217        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1218        resp = await self._request(
1219            RequestMethod.GET, "Destiny2/Clan/ClanBannerDictionary/"
1220        )
1221        assert isinstance(resp, dict)
1222        return resp
1223
1224    async def fetch_public_milestones(self) -> typedefs.JSONObject:
1225        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1226        resp = await self._request(RequestMethod.GET, "Destiny2/Milestones/")
1227        assert isinstance(resp, dict)
1228        return resp
1229
1230    async def fetch_public_milestone_content(
1231        self, milestone_hash: int, /
1232    ) -> typedefs.JSONObject:
1233        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1234        resp = await self._request(
1235            RequestMethod.GET, f"Destiny2/Milestones/{milestone_hash}/Content/"
1236        )
1237        assert isinstance(resp, dict)
1238        return resp
1239
1240    async def fetch_current_user_memberships(
1241        self, access_token: str, /
1242    ) -> typedefs.JSONObject:
1243        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1244        resp = await self._request(
1245            RequestMethod.GET,
1246            "User/GetMembershipsForCurrentUser/",
1247            auth=access_token,
1248        )
1249        assert isinstance(resp, dict)
1250        return resp
1251
1252    async def equip_item(
1253        self,
1254        access_token: str,
1255        /,
1256        item_id: int,
1257        character_id: int,
1258        membership_type: typedefs.IntAnd[enums.MembershipType],
1259    ) -> None:
1260        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1261        payload = {
1262            "itemId": item_id,
1263            "characterId": character_id,
1264            "membershipType": int(membership_type),
1265        }
1266
1267        await self._request(
1268            RequestMethod.POST,
1269            "Destiny2/Actions/Items/EquipItem/",
1270            json=payload,
1271            auth=access_token,
1272        )
1273
1274    async def equip_items(
1275        self,
1276        access_token: str,
1277        /,
1278        item_ids: list[int],
1279        character_id: int,
1280        membership_type: typedefs.IntAnd[enums.MembershipType],
1281    ) -> None:
1282        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1283        payload = {
1284            "itemIds": item_ids,
1285            "characterId": character_id,
1286            "membershipType": int(membership_type),
1287        }
1288        await self._request(
1289            RequestMethod.POST,
1290            "Destiny2/Actions/Items/EquipItems/",
1291            json=payload,
1292            auth=access_token,
1293        )
1294
1295    async def ban_clan_member(
1296        self,
1297        access_token: str,
1298        /,
1299        group_id: int,
1300        membership_id: int,
1301        membership_type: typedefs.IntAnd[enums.MembershipType],
1302        *,
1303        length: int = 0,
1304        comment: undefined.UndefinedOr[str] = undefined.Undefined,
1305    ) -> None:
1306        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1307        payload = {"comment": str(comment), "length": length}
1308        await self._request(
1309            RequestMethod.POST,
1310            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/",
1311            json=payload,
1312            auth=access_token,
1313        )
1314
1315    async def unban_clan_member(
1316        self,
1317        access_token: str,
1318        /,
1319        group_id: int,
1320        membership_id: int,
1321        membership_type: typedefs.IntAnd[enums.MembershipType],
1322    ) -> None:
1323        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1324        await self._request(
1325            RequestMethod.POST,
1326            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/",
1327            auth=access_token,
1328        )
1329
1330    async def kick_clan_member(
1331        self,
1332        access_token: str,
1333        /,
1334        group_id: int,
1335        membership_id: int,
1336        membership_type: typedefs.IntAnd[enums.MembershipType],
1337    ) -> typedefs.JSONObject:
1338        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1339        resp = await self._request(
1340            RequestMethod.POST,
1341            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/",
1342            auth=access_token,
1343        )
1344        assert isinstance(resp, dict)
1345        return resp
1346
1347    async def edit_clan(
1348        self,
1349        access_token: str,
1350        /,
1351        group_id: int,
1352        *,
1353        name: typedefs.NoneOr[str] = None,
1354        about: typedefs.NoneOr[str] = None,
1355        motto: typedefs.NoneOr[str] = None,
1356        theme: typedefs.NoneOr[str] = None,
1357        tags: typedefs.NoneOr[collections.Sequence[str]] = None,
1358        is_public: typedefs.NoneOr[bool] = None,
1359        locale: typedefs.NoneOr[str] = None,
1360        avatar_image_index: typedefs.NoneOr[int] = None,
1361        membership_option: typedefs.NoneOr[
1362            typedefs.IntAnd[enums.MembershipOption]
1363        ] = None,
1364        allow_chat: typedefs.NoneOr[bool] = None,
1365        chat_security: typedefs.NoneOr[typing.Literal[0, 1]] = None,
1366        call_sign: typedefs.NoneOr[str] = None,
1367        homepage: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1368        enable_invite_messaging_for_admins: typedefs.NoneOr[bool] = None,
1369        default_publicity: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1370        is_public_topic_admin: typedefs.NoneOr[bool] = None,
1371    ) -> None:
1372        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1373        payload = {
1374            "name": name,
1375            "about": about,
1376            "motto": motto,
1377            "theme": theme,
1378            "tags": tags,
1379            "isPublic": is_public,
1380            "avatarImageIndex": avatar_image_index,
1381            "isPublicTopicAdminOnly": is_public_topic_admin,
1382            "allowChat": allow_chat,
1383            "chatSecurity": chat_security,
1384            "callsign": call_sign,
1385            "homepage": homepage,
1386            "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins,
1387            "defaultPublicity": default_publicity,
1388            "locale": locale,
1389        }
1390        if membership_option is not None:
1391            payload["membershipOption"] = int(membership_option)
1392
1393        await self._request(
1394            RequestMethod.POST,
1395            f"GroupV2/{group_id}/Edit",
1396            json=payload,
1397            auth=access_token,
1398        )
1399
1400    async def edit_clan_options(
1401        self,
1402        access_token: str,
1403        /,
1404        group_id: int,
1405        *,
1406        invite_permissions_override: typedefs.NoneOr[bool] = None,
1407        update_culture_permissionOverride: typedefs.NoneOr[bool] = None,
1408        host_guided_game_permission_override: typedefs.NoneOr[
1409            typing.Literal[0, 1, 2]
1410        ] = None,
1411        update_banner_permission_override: typedefs.NoneOr[bool] = None,
1412        join_level: typedefs.NoneOr[typedefs.IntAnd[enums.ClanMemberType]] = None,
1413    ) -> None:
1414
1415        payload = {
1416            "InvitePermissionOverride": invite_permissions_override,
1417            "UpdateCulturePermissionOverride": update_culture_permissionOverride,
1418            "HostGuidedGamePermissionOverride": host_guided_game_permission_override,
1419            "UpdateBannerPermissionOverride": update_banner_permission_override,
1420            "JoinLevel": int(join_level) if join_level else None,
1421        }
1422
1423        await self._request(
1424            RequestMethod.POST,
1425            f"GroupV2/{group_id}/EditFounderOptions",
1426            json=payload,
1427            auth=access_token,
1428        )
1429
1430    async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject:
1431        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1432        resp = await self._request(
1433            RequestMethod.GET,
1434            "Social/Friends/",
1435            auth=access_token,
1436        )
1437        assert isinstance(resp, dict)
1438        return resp
1439
1440    async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject:
1441        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1442        resp = await self._request(
1443            RequestMethod.GET,
1444            "Social/Friends/Requests",
1445            auth=access_token,
1446        )
1447        assert isinstance(resp, dict)
1448        return resp
1449
1450    async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1451        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1452        await self._request(
1453            RequestMethod.POST,
1454            f"Social/Friends/Requests/Accept/{member_id}",
1455            auth=access_token,
1456        )
1457
1458    async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1459        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1460        await self._request(
1461            RequestMethod.POST,
1462            f"Social/Friends/Add/{member_id}",
1463            auth=access_token,
1464        )
1465
1466    async def decline_friend_request(
1467        self, access_token: str, /, member_id: int
1468    ) -> None:
1469        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1470        await self._request(
1471            RequestMethod.POST,
1472            f"Social/Friends/Requests/Decline/{member_id}",
1473            auth=access_token,
1474        )
1475
1476    async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1477        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1478        await self._request(
1479            RequestMethod.POST,
1480            f"Social/Friends/Remove/{member_id}",
1481            auth=access_token,
1482        )
1483
1484    async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1485        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1486        await self._request(
1487            RequestMethod.POST,
1488            f"Social/Friends/Requests/Remove/{member_id}",
1489            auth=access_token,
1490        )
1491
1492    async def approve_all_pending_group_users(
1493        self,
1494        access_token: str,
1495        /,
1496        group_id: int,
1497        message: undefined.UndefinedOr[str] = undefined.Undefined,
1498    ) -> None:
1499        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1500        await self._request(
1501            RequestMethod.POST,
1502            f"GroupV2/{group_id}/Members/ApproveAll",
1503            auth=access_token,
1504            json={"message": str(message)},
1505        )
1506
1507    async def deny_all_pending_group_users(
1508        self,
1509        access_token: str,
1510        /,
1511        group_id: int,
1512        *,
1513        message: undefined.UndefinedOr[str] = undefined.Undefined,
1514    ) -> None:
1515        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1516        await self._request(
1517            RequestMethod.POST,
1518            f"GroupV2/{group_id}/Members/DenyAll",
1519            auth=access_token,
1520            json={"message": str(message)},
1521        )
1522
1523    async def add_optional_conversation(
1524        self,
1525        access_token: str,
1526        /,
1527        group_id: int,
1528        *,
1529        name: undefined.UndefinedOr[str] = undefined.Undefined,
1530        security: typing.Literal[0, 1] = 0,
1531    ) -> None:
1532        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1533        payload = {"chatName": str(name), "chatSecurity": security}
1534        await self._request(
1535            RequestMethod.POST,
1536            f"GroupV2/{group_id}/OptionalConversations/Add",
1537            json=payload,
1538            auth=access_token,
1539        )
1540
1541    async def edit_optional_conversation(
1542        self,
1543        access_token: str,
1544        /,
1545        group_id: int,
1546        conversation_id: int,
1547        *,
1548        name: undefined.UndefinedOr[str] = undefined.Undefined,
1549        security: typing.Literal[0, 1] = 0,
1550        enable_chat: bool = False,
1551    ) -> None:
1552        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1553        payload = {
1554            "chatEnabled": enable_chat,
1555            "chatName": str(name),
1556            "chatSecurity": security,
1557        }
1558        await self._request(
1559            RequestMethod.POST,
1560            f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}",
1561            json=payload,
1562            auth=access_token,
1563        )
1564
1565    async def transfer_item(
1566        self,
1567        access_token: str,
1568        /,
1569        item_id: int,
1570        item_hash: int,
1571        character_id: int,
1572        member_type: typedefs.IntAnd[enums.MembershipType],
1573        *,
1574        stack_size: int = 1,
1575        vault: bool = False,
1576    ) -> None:
1577        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1578        payload = {
1579            "characterId": character_id,
1580            "membershipType": int(member_type),
1581            "itemId": item_id,
1582            "itemReferenceHash": item_hash,
1583            "stackSize": stack_size,
1584            "transferToVault": vault,
1585        }
1586        await self._request(
1587            RequestMethod.POST,
1588            "Destiny2/Actions/Items/TransferItem",
1589            json=payload,
1590            auth=access_token,
1591        )
1592
1593    async def pull_item(
1594        self,
1595        access_token: str,
1596        /,
1597        item_id: int,
1598        item_hash: int,
1599        character_id: int,
1600        member_type: typedefs.IntAnd[enums.MembershipType],
1601        *,
1602        stack_size: int = 1,
1603        vault: bool = False,
1604    ) -> None:
1605        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1606        payload = {
1607            "characterId": character_id,
1608            "membershipType": int(member_type),
1609            "itemId": item_id,
1610            "itemReferenceHash": item_hash,
1611            "stackSize": stack_size,
1612            "transferToVault": vault,
1613        }
1614        await self._request(
1615            RequestMethod.POST,
1616            "Destiny2/Actions/Items/PullFromPostmaster",
1617            json=payload,
1618            auth=access_token,
1619        )
1620
1621    async def fetch_fireteams(
1622        self,
1623        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1624        *,
1625        platform: typedefs.IntAnd[
1626            fireteams.FireteamPlatform
1627        ] = fireteams.FireteamPlatform.ANY,
1628        language: typing.Union[
1629            fireteams.FireteamLanguage, str
1630        ] = fireteams.FireteamLanguage.ALL,
1631        date_range: typedefs.IntAnd[
1632            fireteams.FireteamDate
1633        ] = fireteams.FireteamDate.ALL,
1634        page: int = 0,
1635        slots_filter: int = 0,
1636    ) -> typedefs.JSONObject:
1637        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1638        resp = await self._request(
1639            RequestMethod.GET,
1640            f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}",  # noqa: E501 Line too long
1641        )
1642        assert isinstance(resp, dict)
1643        return resp
1644
1645    async def fetch_avaliable_clan_fireteams(
1646        self,
1647        access_token: str,
1648        group_id: int,
1649        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1650        *,
1651        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1652        language: typing.Union[fireteams.FireteamLanguage, str],
1653        date_range: typedefs.IntAnd[
1654            fireteams.FireteamDate
1655        ] = fireteams.FireteamDate.ALL,
1656        page: int = 0,
1657        public_only: bool = False,
1658        slots_filter: int = 0,
1659    ) -> typedefs.JSONObject:
1660        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1661        resp = await self._request(
1662            RequestMethod.GET,
1663            f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}",  # noqa: E501
1664            json={"langFilter": str(language)},
1665            auth=access_token,
1666        )
1667        assert isinstance(resp, dict)
1668        return resp
1669
1670    async def fetch_clan_fireteam(
1671        self, access_token: str, fireteam_id: int, group_id: int
1672    ) -> typedefs.JSONObject:
1673        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1674        resp = await self._request(
1675            RequestMethod.GET,
1676            f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}",
1677            auth=access_token,
1678        )
1679        assert isinstance(resp, dict)
1680        return resp
1681
1682    async def fetch_my_clan_fireteams(
1683        self,
1684        access_token: str,
1685        group_id: int,
1686        *,
1687        include_closed: bool = True,
1688        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1689        language: typing.Union[fireteams.FireteamLanguage, str],
1690        filtered: bool = True,
1691        page: int = 0,
1692    ) -> typedefs.JSONObject:
1693        payload = {"groupFilter": filtered, "langFilter": str(language)}
1694        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1695        resp = await self._request(
1696            RequestMethod.GET,
1697            f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}",
1698            json=payload,
1699            auth=access_token,
1700        )
1701        assert isinstance(resp, dict)
1702        return resp
1703
1704    async def fetch_private_clan_fireteams(
1705        self, access_token: str, group_id: int, /
1706    ) -> int:
1707        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1708        resp = await self._request(
1709            RequestMethod.GET,
1710            f"Fireteam/Clan/{group_id}/ActiveCount",
1711            auth=access_token,
1712        )
1713        assert isinstance(resp, int)
1714        return resp
1715
1716    async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject:
1717        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1718        resp = await self._request(
1719            RequestMethod.GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}"
1720        )
1721        assert isinstance(resp, dict)
1722        return resp
1723
1724    async def search_entities(
1725        self, name: str, entity_type: str, *, page: int = 0
1726    ) -> typedefs.JSONObject:
1727        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1728        resp = await self._request(
1729            RequestMethod.GET,
1730            f"Destiny2/Armory/Search/{entity_type}/{name}/",
1731            json={"page": page},
1732        )
1733        assert isinstance(resp, dict)
1734        return resp
1735
1736    async def fetch_unique_weapon_history(
1737        self,
1738        membership_id: int,
1739        character_id: int,
1740        membership_type: typedefs.IntAnd[enums.MembershipType],
1741    ) -> typedefs.JSONObject:
1742        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1743        resp = await self._request(
1744            RequestMethod.GET,
1745            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/",
1746        )
1747        assert isinstance(resp, dict)
1748        return resp
1749
1750    async def fetch_item(
1751        self,
1752        member_id: int,
1753        item_id: int,
1754        membership_type: typedefs.IntAnd[enums.MembershipType],
1755        components: list[enums.ComponentType],
1756    ) -> typedefs.JSONObject:
1757        collector = _collect_components(components)
1758        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1759        resp = await self._request(
1760            RequestMethod.GET,
1761            f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}",
1762        )
1763        assert isinstance(resp, dict)
1764        return resp
1765
1766    async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject:
1767        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1768        resp = await self._request(
1769            RequestMethod.GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/"
1770        )
1771        assert isinstance(resp, dict)
1772        return resp
1773
1774    async def fetch_available_locales(self) -> typedefs.JSONObject:
1775        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1776        resp = await self._request(
1777            RequestMethod.GET, "Destiny2/Manifest/DestinyLocaleDefinition/"
1778        )
1779        assert isinstance(resp, dict)
1780        return resp
1781
1782    async def fetch_common_settings(self) -> typedefs.JSONObject:
1783        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1784        resp = await self._request(RequestMethod.GET, "Settings")
1785        assert isinstance(resp, dict)
1786        return resp
1787
1788    async def fetch_user_systems_overrides(self) -> typedefs.JSONObject:
1789        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1790        resp = await self._request(RequestMethod.GET, "UserSystemOverrides")
1791        assert isinstance(resp, dict)
1792        return resp
1793
1794    async def fetch_global_alerts(
1795        self, *, include_streaming: bool = False
1796    ) -> typedefs.JSONArray:
1797        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1798        resp = await self._request(
1799            RequestMethod.GET, f"GlobalAlerts/?includestreaming={include_streaming}"
1800        )
1801        assert isinstance(resp, list)
1802        return resp
1803
1804    async def awainitialize_request(
1805        self,
1806        access_token: str,
1807        type: typing.Literal[0, 1],
1808        membership_type: typedefs.IntAnd[enums.MembershipType],
1809        /,
1810        *,
1811        affected_item_id: typing.Optional[int] = None,
1812        character_id: typing.Optional[int] = None,
1813    ) -> typedefs.JSONObject:
1814        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1815
1816        body = {"type": type, "membershipType": int(membership_type)}
1817
1818        if affected_item_id is not None:
1819            body["affectedItemId"] = affected_item_id
1820
1821        if character_id is not None:
1822            body["characterId"] = character_id
1823
1824        resp = await self._request(
1825            RequestMethod.POST, "Destiny2/Awa/Initialize", json=body, auth=access_token
1826        )
1827        assert isinstance(resp, dict)
1828        return resp
1829
1830    async def awaget_action_token(
1831        self, access_token: str, correlation_id: str, /
1832    ) -> typedefs.JSONObject:
1833        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1834        resp = await self._request(
1835            RequestMethod.POST,
1836            f"Destiny2/Awa/GetActionToken/{correlation_id}",
1837            auth=access_token,
1838        )
1839        assert isinstance(resp, dict)
1840        return resp
1841
1842    async def awa_provide_authorization_result(
1843        self,
1844        access_token: str,
1845        selection: int,
1846        correlation_id: str,
1847        nonce: collections.MutableSequence[typing.Union[str, bytes]],
1848    ) -> int:
1849        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1850
1851        body = {"selection": selection, "correlationId": correlation_id, "nonce": nonce}
1852
1853        resp = await self._request(
1854            RequestMethod.POST,
1855            "Destiny2/Awa/AwaProvideAuthorizationResult",
1856            json=body,
1857            auth=access_token,
1858        )
1859        assert isinstance(resp, int)
1860        return resp
1861
1862    async def fetch_vendors(
1863        self,
1864        access_token: str,
1865        character_id: int,
1866        membership_id: int,
1867        membership_type: typedefs.IntAnd[enums.MembershipType],
1868        /,
1869        components: list[enums.ComponentType],
1870        filter: typing.Optional[int] = None,
1871    ) -> typedefs.JSONObject:
1872        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1873        components_ = _collect_components(components)
1874        route = (
1875            f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1876            f"/Character/{character_id}/Vendors/?components={components_}"
1877        )
1878
1879        if filter is not None:
1880            route = route + f"&filter={filter}"
1881
1882        resp = await self._request(
1883            RequestMethod.GET,
1884            route,
1885            auth=access_token,
1886        )
1887        assert isinstance(resp, dict)
1888        return resp
1889
1890    async def fetch_vendor(
1891        self,
1892        access_token: str,
1893        character_id: int,
1894        membership_id: int,
1895        membership_type: typedefs.IntAnd[enums.MembershipType],
1896        vendor_hash: int,
1897        /,
1898        components: list[enums.ComponentType],
1899    ) -> typedefs.JSONObject:
1900        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1901        components_ = _collect_components(components)
1902        resp = await self._request(
1903            RequestMethod.GET,
1904            (
1905                f"Platform/Destiny2/{int(membership_type)}/Profile/{membership_id}"
1906                f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}"
1907            ),
1908            auth=access_token,
1909        )
1910        assert isinstance(resp, dict)
1911        return resp
1912
1913    async def fetch_application_api_usage(
1914        self,
1915        access_token: str,
1916        application_id: int,
1917        /,
1918        *,
1919        start: typing.Optional[datetime.datetime] = None,
1920        end: typing.Optional[datetime.datetime] = None,
1921    ) -> typedefs.JSONObject:
1922
1923        end_date, start_date = time.parse_date_range(end, start)
1924        resp = await self._request(
1925            RequestMethod.GET,
1926            f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}",
1927            auth=access_token,
1928        )
1929        assert isinstance(resp, dict)
1930        return resp
1931
1932    async def fetch_bungie_applications(self) -> typedefs.JSONArray:
1933        resp = await self._request(RequestMethod.GET, "App/FirstParty")
1934        assert isinstance(resp, list)
1935        return resp
1936
1937    async def fetch_content_type(self, type: str, /) -> typedefs.JSONObject:
1938        resp = await self._request(RequestMethod.GET, f"Content/GetContentType/{type}/")
1939        assert isinstance(resp, dict)
1940        return resp
1941
1942    async def fetch_content_by_id(
1943        self, id: int, locale: str, /, *, head: bool = False
1944    ) -> typedefs.JSONObject:
1945        resp = await self._request(
1946            RequestMethod.GET,
1947            f"Content/GetContentById/{id}/{locale}/",
1948            json={"head": head},
1949        )
1950        assert isinstance(resp, dict)
1951        return resp
1952
1953    async def fetch_content_by_tag_and_type(
1954        self, locale: str, tag: str, type: str, *, head: bool = False
1955    ) -> typedefs.JSONObject:
1956        resp = await self._request(
1957            RequestMethod.GET,
1958            f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/",
1959            json={"head": head},
1960        )
1961        assert isinstance(resp, dict)
1962        return resp
1963
1964    async def search_content_with_text(
1965        self,
1966        locale: str,
1967        /,
1968        content_type: str,
1969        search_text: str,
1970        tag: str,
1971        *,
1972        page: undefined.UndefinedOr[int] = undefined.Undefined,
1973        source: undefined.UndefinedOr[str] = undefined.Undefined,
1974    ) -> typedefs.JSONObject:
1975
1976        body: typedefs.JSONObject = {}
1977
1978        body["ctype"] = content_type
1979        body["searchtext"] = search_text
1980        body["tag"] = tag
1981
1982        if page is not undefined.Undefined:
1983            body["currentpage"] = page
1984        else:
1985            body["currentpage"] = 1
1986
1987        if source is not undefined.Undefined:
1988            body["source"] = source
1989        else:
1990            source = ""
1991        resp = await self._request(
1992            RequestMethod.GET, f"Content/Search/{locale}/", json=body
1993        )
1994        assert isinstance(resp, dict)
1995        return resp
1996
1997    async def search_content_by_tag_and_type(
1998        self,
1999        locale: str,
2000        tag: str,
2001        type: str,
2002        *,
2003        page: undefined.UndefinedOr[int] = undefined.Undefined,
2004    ) -> typedefs.JSONObject:
2005        body: typedefs.JSONObject = {}
2006        body["currentpage"] = 1 if page is undefined.Undefined else page
2007        resp = await self._request(
2008            RequestMethod.GET,
2009            f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/",
2010            json=body,
2011        )
2012        assert isinstance(resp, dict)
2013        return resp
2014
2015    async def search_help_articles(
2016        self, text: str, size: str, /
2017    ) -> typedefs.JSONObject:
2018        resp = await self._request(
2019            RequestMethod.GET, f"Content/SearchHelpArticles/{text}/{size}/"
2020        )
2021        assert isinstance(resp, dict)
2022        return resp
2023
2024    async def fetch_topics_page(
2025        self,
2026        category_filter: int,
2027        group: int,
2028        date_filter: int,
2029        sort: typing.Union[str, bytes],
2030        *,
2031        page: undefined.UndefinedOr[int] = undefined.Undefined,
2032        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.Undefined,
2033        tag_filter: undefined.UndefinedOr[str] = undefined.Undefined,
2034    ) -> typedefs.JSONObject:
2035
2036        body: typedefs.JSONObject = {}
2037        if locales is not undefined.Undefined:
2038            body["locales"] = ",".join(str(locales))
2039        else:
2040            body["locales"] = ",".join([])
2041
2042        if tag_filter is not undefined.Undefined:
2043            body["tagstring"] = tag_filter
2044        else:
2045            body["tagstring"] = ""
2046
2047        page = 0 if page is not undefined.Undefined else page
2048
2049        resp = await self._request(
2050            RequestMethod.GET,
2051            f"Forum/GetTopicsPaged/{page}/{0}/{group}/{sort!s}/{date_filter}/{category_filter}/",
2052            json=body,
2053        )
2054        assert isinstance(resp, dict)
2055        return resp
2056
2057    async def fetch_core_topics_page(
2058        self,
2059        category_filter: int,
2060        date_filter: int,
2061        sort: typing.Union[str, bytes],
2062        *,
2063        page: undefined.UndefinedOr[int] = undefined.Undefined,
2064        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.Undefined,
2065    ) -> typedefs.JSONObject:
2066        body: typedefs.JSONObject = {}
2067
2068        if locales is not undefined.Undefined:
2069            body["locales"] = ",".join(str(locales))
2070        else:
2071            body["locales"] = ",".join([])
2072
2073        resp = await self._request(
2074            RequestMethod.GET,
2075            f"Forum/GetCoreTopicsPaged/{0 if page is undefined.Undefined else page}"
2076            f"/{sort!s}/{date_filter}/{category_filter}/",
2077            json=body,
2078        )
2079        assert isinstance(resp, dict)
2080        return resp
2081
2082    async def fetch_posts_threaded_page(
2083        self,
2084        parent_post: bool,
2085        page: int,
2086        page_size: int,
2087        parent_post_id: int,
2088        reply_size: int,
2089        root_thread_mode: bool,
2090        sort_mode: int,
2091        show_banned: typing.Optional[str] = None,
2092    ) -> typedefs.JSONObject:
2093        resp = await self._request(
2094            RequestMethod.GET,
2095            f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/"
2096            f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/",
2097            json={"showbanned": show_banned},
2098        )
2099        assert isinstance(resp, dict)
2100        return resp
2101
2102    async def fetch_posts_threaded_page_from_child(
2103        self,
2104        child_id: bool,
2105        page: int,
2106        page_size: int,
2107        reply_size: int,
2108        root_thread_mode: bool,
2109        sort_mode: int,
2110        show_banned: typing.Optional[str] = None,
2111    ) -> typedefs.JSONObject:
2112        resp = await self._request(
2113            RequestMethod.GET,
2114            f"Forum/GetPostsThreadedPagedFromChild/{child_id}/"
2115            f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/",
2116            json={"showbanned": show_banned},
2117        )
2118        assert isinstance(resp, dict)
2119        return resp
2120
2121    async def fetch_post_and_parent(
2122        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2123    ) -> typedefs.JSONObject:
2124        resp = await self._request(
2125            RequestMethod.GET,
2126            f"Forum/GetPostAndParent/{child_id}/",
2127            json={"showbanned": show_banned},
2128        )
2129        assert isinstance(resp, dict)
2130        return resp
2131
2132    async def fetch_posts_and_parent_awaiting(
2133        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2134    ) -> typedefs.JSONObject:
2135        resp = await self._request(
2136            RequestMethod.GET,
2137            f"Forum/GetPostAndParentAwaitingApproval/{child_id}/",
2138            json={"showbanned": show_banned},
2139        )
2140        assert isinstance(resp, dict)
2141        return resp
2142
2143    async def fetch_topic_for_content(self, content_id: int, /) -> int:
2144        resp = await self._request(
2145            RequestMethod.GET, f"Forum/GetTopicForContent/{content_id}/"
2146        )
2147        assert isinstance(resp, int)
2148        return resp
2149
2150    async def fetch_forum_tag_suggestions(
2151        self, partial_tag: str, /
2152    ) -> typedefs.JSONObject:
2153        resp = await self._request(
2154            RequestMethod.GET,
2155            "Forum/GetForumTagSuggestions/",
2156            json={"partialtag": partial_tag},
2157        )
2158        assert isinstance(resp, dict)
2159        return resp
2160
2161    async def fetch_poll(self, topic_id: int, /) -> typedefs.JSONObject:
2162        resp = await self._request(RequestMethod.GET, f"Forum/Poll/{topic_id}/")
2163        assert isinstance(resp, dict)
2164        return resp
2165
2166    async def fetch_recuirement_thread_summaries(self) -> typedefs.JSONArray:
2167        resp = await self._request(RequestMethod.POST, "Forum/Recruit/Summaries/")
2168        assert isinstance(resp, list)
2169        return resp
2170
2171    async def fetch_recommended_groups(
2172        self,
2173        accecss_token: str,
2174        /,
2175        *,
2176        date_range: int = 0,
2177        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
2178    ) -> typedefs.JSONArray:
2179        resp = await self._request(
2180            RequestMethod.POST,
2181            f"GroupV2/Recommended/{int(group_type)}/{date_range}/",
2182            auth=accecss_token,
2183        )
2184        assert isinstance(resp, list)
2185        return resp
2186
2187    async def fetch_available_avatars(self) -> collections.Mapping[str, int]:
2188        resp = await self._request(RequestMethod.GET, "GroupV2/GetAvailableAvatars/")
2189        assert isinstance(resp, dict)
2190        return resp
2191
2192    async def fetch_user_clan_invite_setting(
2193        self,
2194        access_token: str,
2195        /,
2196        membership_type: typedefs.IntAnd[enums.MembershipType],
2197    ) -> bool:
2198        resp = await self._request(
2199            RequestMethod.GET,
2200            f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/",
2201            auth=access_token,
2202        )
2203        assert isinstance(resp, bool)
2204        return resp
2205
2206    async def fetch_banned_group_members(
2207        self, access_token: str, group_id: int, /, *, page: int = 1
2208    ) -> typedefs.JSONObject:
2209        resp = await self._request(
2210            RequestMethod.GET,
2211            f"GroupV2/{group_id}/Banned/?currentpage={page}",
2212            auth=access_token,
2213        )
2214        assert isinstance(resp, dict)
2215        return resp
2216
2217    async def fetch_pending_group_memberships(
2218        self, access_token: str, group_id: int, /, *, current_page: int = 1
2219    ) -> typedefs.JSONObject:
2220        resp = await self._request(
2221            RequestMethod.GET,
2222            f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}",
2223            auth=access_token,
2224        )
2225        assert isinstance(resp, dict)
2226        return resp
2227
2228    async def fetch_invited_group_memberships(
2229        self, access_token: str, group_id: int, /, *, current_page: int = 1
2230    ) -> typedefs.JSONObject:
2231        resp = await self._request(
2232            RequestMethod.GET,
2233            f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}",
2234            auth=access_token,
2235        )
2236        assert isinstance(resp, dict)
2237        return resp
2238
2239    async def invite_member_to_group(
2240        self,
2241        access_token: str,
2242        /,
2243        group_id: int,
2244        membership_id: int,
2245        membership_type: typedefs.IntAnd[enums.MembershipType],
2246        *,
2247        message: undefined.UndefinedOr[str] = undefined.Undefined,
2248    ) -> typedefs.JSONObject:
2249        resp = await self._request(
2250            RequestMethod.POST,
2251            f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/",
2252            auth=access_token,
2253            json={"message": str(message)},
2254        )
2255        assert isinstance(resp, dict)
2256        return resp
2257
2258    async def cancel_group_member_invite(
2259        self,
2260        access_token: str,
2261        /,
2262        group_id: int,
2263        membership_id: int,
2264        membership_type: typedefs.IntAnd[enums.MembershipType],
2265    ) -> typedefs.JSONObject:
2266        resp = await self._request(
2267            RequestMethod.POST,
2268            f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/",
2269            auth=access_token,
2270        )
2271        assert isinstance(resp, dict)
2272        return resp
2273
2274    async def fetch_historical_definition(self) -> typedefs.JSONObject:
2275        resp = await self._request(RequestMethod.GET, "Destiny2/Stats/Definition/")
2276        assert isinstance(resp, dict)
2277        return resp
2278
2279    async def fetch_historical_stats(
2280        self,
2281        character_id: int,
2282        membership_id: int,
2283        membership_type: typedefs.IntAnd[enums.MembershipType],
2284        day_start: datetime.datetime,
2285        day_end: datetime.datetime,
2286        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2287        modes: collections.Sequence[typedefs.IntAnd[enums.GameMode]],
2288        *,
2289        period_type: enums.PeriodType = enums.PeriodType.ALL_TIME,
2290    ) -> typedefs.JSONObject:
2291
2292        end, start = time.parse_date_range(day_end, day_start)
2293        resp = await self._request(
2294            RequestMethod.GET,
2295            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/",
2296            json={
2297                "dayend": end,
2298                "daystart": start,
2299                "groups": [str(int(group)) for group in groups],
2300                "modes": [str(int(mode)) for mode in modes],
2301                "periodType": int(period_type),
2302            },
2303        )
2304        assert isinstance(resp, dict)
2305        return resp
2306
2307    async def fetch_historical_stats_for_account(
2308        self,
2309        membership_id: int,
2310        membership_type: typedefs.IntAnd[enums.MembershipType],
2311        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2312    ) -> typedefs.JSONObject:
2313        resp = await self._request(
2314            RequestMethod.GET,
2315            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/",
2316            json={"groups": [str(int(group)) for group in groups]},
2317        )
2318        assert isinstance(resp, dict)
2319        return resp
2320
2321    async def fetch_aggregated_activity_stats(
2322        self,
2323        character_id: int,
2324        membership_id: int,
2325        membership_type: typedefs.IntAnd[enums.MembershipType],
2326        /,
2327    ) -> typedefs.JSONObject:
2328        resp = await self._request(
2329            RequestMethod.GET,
2330            f"Destiny2/{int(membership_type)}/Account/{membership_id}/"
2331            f"Character/{character_id}/Stats/AggregateActivityStats/",
2332        )
2333        assert isinstance(resp, dict)
2334        return resp

A RESTful client implementation for Bungie's API.

This client is designed to only make HTTP requests and return JSON objects to provide RESTful functionality.

This client is also used within aiobungie.Client which deserialize those returned JSON objects using the factory into Pythonic data classes objects which provide Python functionality.

Example
import aiobungie

async def main():
    async with aiobungie.RESTClient("TOKEN") as rest_client:
        req = await rest_client.fetch_clan_members(4389205)
        clan_members = req['results']
        for member in clan_members:
            for k, v in member['destinyUserInfo'].items():
                print(k, v)
Parameters
  • token (str): A valid application token from Bungie's developer portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • max_ratelimit_retries (int): The max retries number to retry if the request hit a 429 status code. Defaults to 3.
  • client_secret (typing.Optional[str]): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (typing.Optional[int]): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
  • enable_debugging (bool | str): Whether to enable logging responses or not.
Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information.
  • "TRACE" | aiobungie.TRACE: This will log the response headers along with the minimal information.
RESTClient( token: str, /, client_secret: Optional[str] = None, client_id: Optional[int] = None, *, max_retries: int = 4, max_ratelimit_retries: int = 3, enable_debugging: Union[Literal['TRACE'], bool, int] = False)
413    def __init__(
414        self,
415        token: str,
416        /,
417        client_secret: typing.Optional[str] = None,
418        client_id: typing.Optional[int] = None,
419        *,
420        max_retries: int = 4,
421        max_ratelimit_retries: int = 3,
422        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
423    ) -> None:
424        self._session: typing.Optional[_Session] = None
425        self._lock: typing.Optional[asyncio.Lock] = None
426        self._client_secret = client_secret
427        self._client_id = client_id
428        self._token: str = token
429        self._max_retries = max_retries
430        self._max_rate_limit_retries = max_ratelimit_retries
431        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
432
433        self._set_debug_level(enable_debugging)
client_id: Optional[int]

Return the client id of this REST client if provided, Otherwise None.

metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

A mutable mapping storage for the user's needs.

This mapping is useful for storing any kind of data that the user may need.

Example
import aiobungie

client = aiobungie.RESTClient(…)

async with client:
    # Fetch auth tokens and store them
    client.metadata["tokens"] = await client.fetch_access_token("code")

# Some other time.
async with client:
    # Retrieve the tokens
    tokens: aiobungie.OAuth2Response = client.metadata["tokens"]

    # Use them to fetch your user.
    user = await client.fetch_current_user_memberships(tokens.access_token)
is_alive: bool

Returns True if the REST client is alive and False otherwise.

@typing.final
async def close(self) -> None:
447    @typing.final
448    async def close(self) -> None:
449        if self._session is not None:
450            await self._session.close()
451            self._session = None

Close this REST client session if it was acquired.

@typing.final
def enable_debugging( self, level: Union[Literal['TRACE'], bool, int] = False, file: Union[pathlib.Path, str, NoneType] = None, /) -> None:
453    @typing.final
454    def enable_debugging(
455        self,
456        level: typing.Union[typing.Literal["TRACE"], bool, int] = False,
457        file: typing.Optional[typing.Union[pathlib.Path, str]] = None,
458        /,
459    ) -> None:
460        self._set_debug_level(level, file)

Enables debugging for the REST calls.

Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information.
  • "TRACE" | aiobungie.TRACE: This will log the response headers along with the minimal information.
Parameters
  • level (str | bool | int): The level of debugging to enable.
  • file (pathlib.Path | str | None): The file path to write the debug logs to. If provided.
@typing.final
async def static_request( self, method: Union[aiobungie.RequestMethod, str], path: str, *, auth: Optional[str] = None, json: Optional[dict[str, Any]] = None) -> Union[dict[str, Any], list[Any], bytes, int, bool, NoneType]:
462    @typing.final
463    async def static_request(
464        self,
465        method: typing.Union[RequestMethod, str],
466        path: str,
467        *,
468        auth: typing.Optional[str] = None,
469        json: typing.Optional[dict[str, typing.Any]] = None,
470    ) -> ResponseSig:
471        return await self._request(method, path, auth=auth, json=json)

Perform an HTTP request given a valid Bungie endpoint.

Parameters
  • method (aiobungie.RequestMethod | str): The request method, This may be GET, POST, PUT, etc.
  • path (str): The Bungie endpoint or path. A path must look something like this Destiny2/3/Profile/46111239123/...
  • auth (str | None): An optional bearer token for methods that requires OAuth2 Authorization header.
  • json (dict[str, typing.Any] | None): An optional JSON data to include in the request.
Returns
  • aiobungie.rest.ResponseSig: The response payload.
@typing.final
def build_oauth2_url(self, client_id: Optional[int] = None) -> Optional[str]:
473    @typing.final
474    def build_oauth2_url(
475        self, client_id: typing.Optional[int] = None
476    ) -> typing.Optional[str]:
477        client_id = client_id or self._client_id
478        if client_id is None:
479            return None
480
481        return url.OAUTH2_EP_BUILDER.format(
482            oauth_endpoint=url.OAUTH_EP,
483            client_id=client_id,
484            uuid=_uuid(),
485        )

Builds an OAuth2 URL using the provided user REST/Base client secret/id.

Parameters
  • client_id (int | None): An optional client id to provide, If left None it will roll back to the id passed to the RESTClient, If both is None this method will return None.
Returns
  • str | None: If the client id was provided as a parameter or provided in aiobungie.RESTClient, A complete URL will be returned. Otherwise None will be returned.
async def fetch_oauth2_tokens(self, code: str, /) -> aiobungie.builders.OAuth2Response:
717    async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response:
718
719        if not isinstance(self._client_id, int):
720            raise TypeError(
721                f"Expected (int) for client id but got {type(self._client_id).__qualname__}"  # type: ignore
722            )
723
724        if not isinstance(self._client_secret, str):
725            raise TypeError(
726                f"Expected (str) for client secret but got {type(self._client_secret).__qualname__}"  # type: ignore
727            )
728
729        headers = {
730            "client_secret": self._client_secret,
731        }
732
733        data = (
734            f"grant_type=authorization_code&code={code}"
735            f"&client_id={self._client_id}&client_secret={self._client_secret}"
736        )
737
738        response = await self._request(
739            RequestMethod.POST, "", headers=headers, data=data, oauth2=True
740        )
741        assert isinstance(response, dict)
742        return builders.OAuth2Response.build_response(response)

Makes a POST request and fetch the OAuth2 access_token and refresh token.

Parameters
  • code (str): The Authorization code received from the authorization endpoint found in the URL parameters.
Returns
Raises
async def refresh_access_token(self, refresh_token: str, /) -> aiobungie.builders.OAuth2Response:
744    async def refresh_access_token(
745        self, refresh_token: str, /
746    ) -> builders.OAuth2Response:
747        if not isinstance(self._client_id, int):
748            raise TypeError(
749                f"Expected (int) for client id but got {type(self._client_id).__qualname__}"  # type: ignore
750            )
751
752        if not isinstance(self._client_secret, str):
753            raise TypeError(
754                f"Expected (str) for client secret but got {type(self._client_secret).__qualname__}"  # type: ignore
755            )
756
757        data = {
758            "grant_type": "refresh_token",
759            "refresh_token": refresh_token,
760            "client_id": self._client_id,
761            "client_secret": self._client_secret,
762            "Content-Type": "application/x-www-form-urlencoded",
763        }
764
765        response = await self._request(RequestMethod.POST, "", data=data, oauth2=True)
766        assert isinstance(response, dict)
767        return builders.OAuth2Response.build_response(response)

Refresh OAuth2 access token given its refresh token.

Parameters
  • refresh_token (str): The refresh token.
Returns
async def fetch_bungie_user(self, id: int) -> dict[str, typing.Any]:
769    async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject:
770        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
771        resp = await self._request(
772            RequestMethod.GET, f"User/GetBungieNetUserById/{id}/"
773        )
774        assert isinstance(resp, dict)
775        return resp

Fetch a Bungie user by their id.

Parameters
  • id (int): The user id.
Returns
Raises
async def fetch_user_themes(self) -> list[typing.Any]:
777    async def fetch_user_themes(self) -> typedefs.JSONArray:
778        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
779        resp = await self._request(RequestMethod.GET, "User/GetAvailableThemes/")
780        assert isinstance(resp, list)
781        return resp

Fetch all available user themes.

Returns
async def fetch_membership_from_id( self, id: int, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>, /) -> dict[str, typing.Any]:
783    async def fetch_membership_from_id(
784        self,
785        id: int,
786        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
787        /,
788    ) -> typedefs.JSONObject:
789        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
790        resp = await self._request(
791            RequestMethod.GET, f"User/GetMembershipsById/{id}/{int(type)}"
792        )
793        assert isinstance(resp, dict)
794        return resp

Fetch Bungie user's memberships from their id.

Parameters
Returns
Raises
async def fetch_player( self, name: str, code: int, type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>, /) -> list[typing.Any]:
796    async def fetch_player(
797        self,
798        name: str,
799        code: int,
800        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
801        /,
802    ) -> typedefs.JSONArray:
803        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
804        resp = await self._request(
805            RequestMethod.POST,
806            f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}",
807            json={"displayName": name, "displayNameCode": code},
808        )
809        assert isinstance(resp, list)
810        return resp

Fetch a Destiny 2 Player.

Parameters
Returns
Raises
async def search_users(self, name: str, /) -> dict[str, typing.Any]:
812    async def search_users(self, name: str, /) -> typedefs.JSONObject:
813        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
814        resp = await self._request(
815            RequestMethod.POST,
816            "User/Search/GlobalName/0",
817            json={"displayNamePrefix": name},
818        )
819        assert isinstance(resp, dict)
820        return resp

Search for users by their global name and return all users who share this name.

Parameters
  • name (str): The user name.
Returns
Raises
async def fetch_clan_from_id( self, id: int, /, access_token: Optional[str] = None) -> dict[str, typing.Any]:
822    async def fetch_clan_from_id(
823        self, id: int, /, access_token: typing.Optional[str] = None
824    ) -> typedefs.JSONObject:
825        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
826        resp = await self._request(
827            RequestMethod.GET, f"GroupV2/{id}", auth=access_token
828        )
829        assert isinstance(resp, dict)
830        return resp

Fetch a Bungie Clan by its id.

Parameters
  • id (int): The clan id.
Other Parameters
  • access_token (typing.Optional[str]): An optional access token to make the request with.

    If the token was bound to a member of the clan, This field aiobungie.crates.Clan.current_user_membership will be available and will return the membership of the user who made this request.

Returns
Raises
async def fetch_clan( self, name: str, /, access_token: Optional[str] = None, *, type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> dict[str, typing.Any]:
832    async def fetch_clan(
833        self,
834        name: str,
835        /,
836        access_token: typing.Optional[str] = None,
837        *,
838        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
839    ) -> typedefs.JSONObject:
840        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
841        resp = await self._request(
842            RequestMethod.GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token
843        )
844        assert isinstance(resp, dict)
845        return resp

Fetch a Clan by its name. This method will return the first clan found with given name name.

Parameters
  • name (str): The clan name.
Other Parameters
Returns
Raises
async def fetch_clan_admins(self, clan_id: int, /) -> dict[str, typing.Any]:
847    async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject:
848        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
849        resp = await self._request(
850            RequestMethod.GET, f"GroupV2/{clan_id}/AdminsAndFounder/"
851        )
852        assert isinstance(resp, dict)
853        return resp

Fetch the admins and founder members of the clan.

Parameters
  • clan_id (int): The clan id.
Returns
Raises
async def fetch_clan_conversations(self, clan_id: int, /) -> list[typing.Any]:
855    async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray:
856        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
857        resp = await self._request(
858            RequestMethod.GET, f"GroupV2/{clan_id}/OptionalConversations/"
859        )
860        assert isinstance(resp, list)
861        return resp

Fetch a clan's conversations.

Parameters
  • clan_id (int): The clan's id.
Returns
async def fetch_application(self, appid: int, /) -> dict[str, typing.Any]:
863    async def fetch_application(self, appid: int, /) -> typedefs.JSONObject:
864        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
865        resp = await self._request(RequestMethod.GET, f"App/Application/{appid}")
866        assert isinstance(resp, dict)
867        return resp

Fetch a Bungie Application.

Parameters
  • appid (int): The application id.
Returns
async def fetch_character( self, member_id: int, membership_type: Union[int, aiobungie.MembershipType], character_id: int, components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> dict[str, typing.Any]:
869    async def fetch_character(
870        self,
871        member_id: int,
872        membership_type: typedefs.IntAnd[enums.MembershipType],
873        character_id: int,
874        components: list[enums.ComponentType],
875        auth: typing.Optional[str] = None,
876    ) -> typedefs.JSONObject:
877        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
878        collector = _collect_components(components)
879        response = await self._request(
880            RequestMethod.GET,
881            f"Destiny2/{int(membership_type)}/Profile/{member_id}/"
882            f"Character/{character_id}/?components={collector}",
883            auth=auth,
884        )
885        assert isinstance(response, dict)
886        return response

Fetch a Destiny 2 player's characters.

Parameters
Other Parameters
  • auth (typing.Optional[str]): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
Raises
async def fetch_activities( self, member_id: int, character_id: int, mode: Union[int, aiobungie.GameMode], membership_type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>, *, page: int = 0, limit: int = 1) -> dict[str, typing.Any]:
888    async def fetch_activities(
889        self,
890        member_id: int,
891        character_id: int,
892        mode: typedefs.IntAnd[enums.GameMode],
893        membership_type: typedefs.IntAnd[
894            enums.MembershipType
895        ] = enums.MembershipType.ALL,
896        *,
897        page: int = 0,
898        limit: int = 1,
899    ) -> typedefs.JSONObject:
900        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
901        resp = await self._request(
902            RequestMethod.GET,
903            f"Destiny2/{int(membership_type)}/Account/"
904            f"{member_id}/Character/{character_id}/Stats/Activities"
905            f"/?mode={int(mode)}&count={limit}&page={page}",
906        )
907        assert isinstance(resp, dict)
908        return resp

Fetch a Destiny 2 activity for the specified user id and character.

Parameters
  • member_id (int): The user id that starts with 4611.
  • character_id (int): The id of the character to retrieve.
  • mode (aiobungie.typedefs.IntAnd[aiobungie.GameMode]): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
Returns
Raises
async def fetch_vendor_sales(self) -> dict[str, typing.Any]:
910    async def fetch_vendor_sales(self) -> typedefs.JSONObject:
911        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
912        resp = await self._request(
913            RequestMethod.GET,
914            f"Destiny2/Vendors/?components={int(enums.ComponentType.VENDOR_SALES)}",
915        )
916        assert isinstance(resp, dict)
917        return resp
async def fetch_profile( self, membership_id: int, type: Union[int, aiobungie.MembershipType], components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> dict[str, typing.Any]:
919    async def fetch_profile(
920        self,
921        membership_id: int,
922        type: typedefs.IntAnd[enums.MembershipType],
923        components: list[enums.ComponentType],
924        auth: typing.Optional[str] = None,
925    ) -> typedefs.JSONObject:
926        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
927        collector = _collect_components(components)
928        response = await self._request(
929            RequestMethod.GET,
930            f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}",
931            auth=auth,
932        )
933        assert isinstance(response, dict)
934        return response

Fetch a bungie profile.

Parameters
Other Parameters
  • auth (typing.Optional[str]): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
Raises
async def fetch_entity(self, type: str, hash: int) -> dict[str, typing.Any]:
936    async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject:
937        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
938        response = await self._request(
939            RequestMethod.GET, route=f"Destiny2/Manifest/{type}/{hash}"
940        )
941        assert isinstance(response, dict)
942        return response

Fetch a Destiny definition item given its type and hash.

Parameters
  • type (str): Entity's type definition.
  • hash (int): Entity's hash.
Returns
async def fetch_inventory_item(self, hash: int, /) -> dict[str, typing.Any]:
944    async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject:
945        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
946        resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash)
947        assert isinstance(resp, dict)
948        return resp

Fetch a Destiny inventory item entity given a its hash.

Parameters
  • hash (int): Entity's hash.
Returns
async def fetch_objective_entity(self, hash: int, /) -> dict[str, typing.Any]:
950    async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject:
951        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
952        resp = await self.fetch_entity("DestinyObjectiveDefinition", hash)
953        assert isinstance(resp, dict)
954        return resp

Fetch a Destiny objective entity given a its hash.

Parameters
  • hash (int): objective's hash.
Returns
async def fetch_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> dict[str, typing.Any]:
956    async def fetch_groups_for_member(
957        self,
958        member_id: int,
959        member_type: typedefs.IntAnd[enums.MembershipType],
960        /,
961        *,
962        filter: int = 0,
963        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
964    ) -> typedefs.JSONObject:
965        resp = await self._request(
966            RequestMethod.GET,
967            f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
968        )
969        assert isinstance(resp, dict)
970        return resp

Fetch the information about the groups for a member.

Parameters
Other Parameters
Returns
async def fetch_potential_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> dict[str, typing.Any]:
972    async def fetch_potential_groups_for_member(
973        self,
974        member_id: int,
975        member_type: typedefs.IntAnd[enums.MembershipType],
976        /,
977        *,
978        filter: int = 0,
979        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
980    ) -> typedefs.JSONObject:
981        resp = await self._request(
982            RequestMethod.GET,
983            f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
984        )
985        assert isinstance(resp, dict)
986        return resp

Get information about the groups that a given member has applied to or been invited to.

Parameters
Other Parameters
Returns
async def fetch_clan_members( self, clan_id: int, /, *, name: Optional[str] = None, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>) -> dict[str, typing.Any]:
 988    async def fetch_clan_members(
 989        self,
 990        clan_id: int,
 991        /,
 992        *,
 993        name: typing.Optional[str] = None,
 994        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 995    ) -> typedefs.JSONObject:
 996        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 997        resp = await self._request(
 998            RequestMethod.GET,
 999            f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}&currentpage=1",
1000        )
1001        assert isinstance(resp, dict)
1002        return resp

Fetch all Bungie Clan members.

Parameters
  • clan_id (builsins.int): The clans id
Other Parameters
Returns
Raises
async def fetch_hardlinked_credentials( self, credential: int, type: Union[int, aiobungie.CredentialType] = <CredentialType.STEAMID: 12>, /) -> dict[str, typing.Any]:
1004    async def fetch_hardlinked_credentials(
1005        self,
1006        credential: int,
1007        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
1008        /,
1009    ) -> typedefs.JSONObject:
1010        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1011        resp = await self._request(
1012            RequestMethod.GET,
1013            f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/",
1014        )
1015        assert isinstance(resp, dict)
1016        return resp

Gets any hard linked membership given a credential.

Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now. Cross Save aware.

Parameters
Returns
async def fetch_user_credentials(self, access_token: str, membership_id: int, /) -> list[typing.Any]:
1018    async def fetch_user_credentials(
1019        self, access_token: str, membership_id: int, /
1020    ) -> typedefs.JSONArray:
1021        resp = await self._request(
1022            RequestMethod.GET,
1023            f"User/GetCredentialTypesForTargetAccount/{membership_id}",
1024            auth=access_token,
1025        )
1026        assert isinstance(resp, list)
1027        return resp

Fetch an array of credential types attached to the requested account.

This method require OAuth2 Bearer access token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • membership_id (int): The id of the membership to return.
Returns
Raises
async def insert_socket_plug( self, action_token: str, /, instance_id: int, plug: Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]], character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
1029    async def insert_socket_plug(
1030        self,
1031        action_token: str,
1032        /,
1033        instance_id: int,
1034        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1035        character_id: int,
1036        membership_type: typedefs.IntAnd[enums.MembershipType],
1037    ) -> typedefs.JSONObject:
1038
1039        if isinstance(plug, builders.PlugSocketBuilder):
1040            plug = plug.collect()
1041
1042        body = {
1043            "actionToken": action_token,
1044            "itemInstanceId": instance_id,
1045            "plug": plug,
1046            "characterId": character_id,
1047            "membershipType": int(membership_type),
1048        }
1049        resp = await self._request(
1050            RequestMethod.POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body
1051        )
1052        assert isinstance(resp, dict)
1053        return resp

Insert a plug into a socketed item.

OAuth2: AdvancedWriteActions scope is required

Parameters
  • action_token (str): Action token provided by the AwaGetActionToken API call.
  • instance_id (int): The item instance id that's plug inserted.
  • plug (typing.Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
    aiobungie.PlugSocketBuilder()
    .set_socket_array(0)
    .set_socket_index(0)
    .set_plug_item(3023847)
    .collect()
)
await insert_socket_plug_free(..., plug=plug)

character_id : int The character's id. membership_type : aiobungie.typedefs.IntAnd[aiobungie.MembershipType] The membership type.

Returns
Raises
async def insert_socket_plug_free( self, access_token: str, /, instance_id: int, plug: Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]], character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
1055    async def insert_socket_plug_free(
1056        self,
1057        access_token: str,
1058        /,
1059        instance_id: int,
1060        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1061        character_id: int,
1062        membership_type: typedefs.IntAnd[enums.MembershipType],
1063    ) -> typedefs.JSONObject:
1064
1065        if isinstance(plug, builders.PlugSocketBuilder):
1066            plug = plug.collect()
1067
1068        body = {
1069            "itemInstanceId": instance_id,
1070            "plug": plug,
1071            "characterId": character_id,
1072            "membershipType": int(membership_type),
1073        }
1074        resp = await self._request(
1075            RequestMethod.POST,
1076            "Destiny2/Actions/Items/InsertSocketPlugFree",
1077            json=body,
1078            auth=access_token,
1079        )
1080        assert isinstance(resp, dict)
1081        return resp

Insert a plug into a socketed item. This doesn't require an Action token.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • instance_id (int): The item instance id that's plug inserted.
  • plug (typing.Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
    aiobungie.PlugSocketBuilder()
    .set_socket_array(0)
    .set_socket_index(0)
    .set_plug_item(3023847)
    .collect()
)
await insert_socket_plug_free(..., plug=plug)

character_id : int The character's id. membership_type : aiobungie.typedefs.IntAnd[aiobungie.MembershipType] The membership type.

Returns
Raises
async def set_item_lock_state( self, access_token: str, state: bool, /, item_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> int:
1083    async def set_item_lock_state(
1084        self,
1085        access_token: str,
1086        state: bool,
1087        /,
1088        item_id: int,
1089        character_id: int,
1090        membership_type: typedefs.IntAnd[enums.MembershipType],
1091    ) -> int:
1092        body = {
1093            "state": state,
1094            "itemId": item_id,
1095            "characterId": character_id,
1096            "membership_type": int(membership_type),
1097        }
1098        response = await self._request(
1099            RequestMethod.POST,
1100            "Destiny2/Actions/Items/SetLockState",
1101            json=body,
1102            auth=access_token,
1103        )
1104        assert isinstance(response, int)
1105        return response

Set the Lock State for an instanced item.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • state (bool): If True, The item will be locked, If False, The item will be unlocked.
  • item_id (int): The item id.
  • character_id (int): The character id.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type for the associated account.
Returns
  • int: An integer represents whether the request was successful or failed.
Raises
async def set_quest_track_state( self, access_token: str, state: bool, /, item_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> int:
1107    async def set_quest_track_state(
1108        self,
1109        access_token: str,
1110        state: bool,
1111        /,
1112        item_id: int,
1113        character_id: int,
1114        membership_type: typedefs.IntAnd[enums.MembershipType],
1115    ) -> int:
1116        body = {
1117            "state": state,
1118            "itemId": item_id,
1119            "characterId": character_id,
1120            "membership_type": int(membership_type),
1121        }
1122        response = await self._request(
1123            RequestMethod.POST,
1124            "Destiny2/Actions/Items/SetTrackedState",
1125            json=body,
1126            auth=access_token,
1127        )
1128        assert isinstance(response, int)
1129        return response

Set the Tracking State for an instanced Quest or Bounty.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • state (bool): If True, The item will be locked, If False, The item will be unlocked.
  • item_id (int): The item id.
  • character_id (int): The character id.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type for the associated account.
Returns
  • int: An integer represents whether the request was successful or failed.
Raises
async def fetch_manifest_path(self) -> dict[str, typing.Any]:
1131    async def fetch_manifest_path(self) -> typedefs.JSONObject:
1132        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1133        path = await self._request(RequestMethod.GET, "Destiny2/Manifest")
1134        assert isinstance(path, dict)
1135        return path

Fetch the manifest JSON paths.

Returns
  • typedefs.JSONObject: The manifest JSON paths.
async def read_manifest_bytes(self, language: str = 'en', /) -> bytes:
1137    async def read_manifest_bytes(self, language: str = "en", /) -> bytes:
1138        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1139        _ensure_manifest_language(language)
1140
1141        content = await self.fetch_manifest_path()
1142        resp = await self._request(
1143            RequestMethod.GET,
1144            content["mobileWorldContentPaths"][language],
1145            unwrapping="read",
1146            base=True,
1147        )
1148        assert isinstance(resp, bytes)
1149        return resp

Read raw manifest SQLite database bytes response.

This method can be used to write the bytes to zipped file and then extract it to get the manifest content.

Parameters
  • language (str): The manifest database language bytes to get.
Returns
  • bytes: The bytes to read and write the manifest database.
async def download_manifest( self, language: str = 'en', name: str = 'manifest.sqlite3', *, force: bool = False) -> None:
1151    async def download_manifest(
1152        self,
1153        language: str = "en",
1154        name: str = "manifest.sqlite3",
1155        *,
1156        force: bool = False,
1157    ) -> None:
1158        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1159        if os.path.exists(name):
1160
1161            if force:
1162                _LOG.debug("Forcing manifest download.")
1163                os.remove(name)
1164
1165                return await self.download_manifest(language, name, force=force)
1166
1167            else:
1168                raise FileExistsError(
1169                    "Manifest file already exists, "
1170                    "If you want to force download, set the `force` parameter to `True`."
1171                )
1172
1173        _LOG.debug("Downloading manifest...")
1174        data_bytes = await self.read_manifest_bytes(language)
1175        await asyncio.get_running_loop().run_in_executor(
1176            None, _write_sqlite_bytes, data_bytes, name
1177        )

A helper method to download the manifest.

Note

This method downloads the sqlite database and not JSON. Use RESTInterface.download_json_manifest for the JSON version.

Parameters
  • language (str): The manifest language to download, Default is english.
  • name (str): The manifest database file name. Default is manifest.sqlite3
  • force (bool): Whether to force the download. Default is False. However if set to true the old file will get removed and a new one will being to download.
Returns
  • None
async def download_json_manifest(self, language: str = 'en') -> None:
1179    async def download_json_manifest(self, language: str = "en") -> None:
1180        _ensure_manifest_language(language)
1181
1182        _LOG.debug("Downloading manifest JSON...")
1183
1184        content = await self.fetch_manifest_path()
1185        json_bytes = await self._request(
1186            RequestMethod.GET,
1187            content["jsonWorldContentPaths"][language],
1188            unwrapping="read",
1189            base=True,
1190        )
1191
1192        await asyncio.get_running_loop().run_in_executor(
1193            None, _write_json_bytes, json_bytes
1194        )
1195        _LOG.debug("Finished downloading manifest JSON.")

Download the Bungie manifest json file.

Parameters
  • language (str): The language to download the manifest in.
async def fetch_manifest_version(self) -> str:
1197    async def fetch_manifest_version(self) -> str:
1198        return typing.cast(str, (await self.fetch_manifest_path())["version"])

Fetch the manifest version.

Returns
  • str: The manifest version.
async def fetch_linked_profiles( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, all: bool = False) -> dict[str, typing.Any]:
1200    async def fetch_linked_profiles(
1201        self,
1202        member_id: int,
1203        member_type: typedefs.IntAnd[enums.MembershipType],
1204        /,
1205        *,
1206        all: bool = False,
1207    ) -> typedefs.JSONObject:
1208        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1209        resp = await self._request(
1210            RequestMethod.GET,
1211            f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}",
1212        )
1213        assert isinstance(resp, dict)
1214        return resp

Returns a summary information about all profiles linked to the requested member.

The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.

It will only return linked accounts whose linkages you are allowed to view.

Parameters
Other Parameters
  • all (bool): If provided and set to True, All memberships regardless of whether thry're obscured by overrides will be returned,

    If provided and set to False, Only available memberships will be returned. The default for this is False.

Returns
async def fetch_clan_banners(self) -> dict[str, typing.Any]:
1216    async def fetch_clan_banners(self) -> typedefs.JSONObject:
1217        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1218        resp = await self._request(
1219            RequestMethod.GET, "Destiny2/Clan/ClanBannerDictionary/"
1220        )
1221        assert isinstance(resp, dict)
1222        return resp

Fetch the values of the clan banners.

Returns
async def fetch_public_milestones(self) -> dict[str, typing.Any]:
1224    async def fetch_public_milestones(self) -> typedefs.JSONObject:
1225        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1226        resp = await self._request(RequestMethod.GET, "Destiny2/Milestones/")
1227        assert isinstance(resp, dict)
1228        return resp

Fetch the available milestones.

Returns
async def fetch_public_milestone_content(self, milestone_hash: int, /) -> dict[str, typing.Any]:
1230    async def fetch_public_milestone_content(
1231        self, milestone_hash: int, /
1232    ) -> typedefs.JSONObject:
1233        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1234        resp = await self._request(
1235            RequestMethod.GET, f"Destiny2/Milestones/{milestone_hash}/Content/"
1236        )
1237        assert isinstance(resp, dict)
1238        return resp

Fetch the milestone content given its hash.

Parameters
  • milestone_hash (int): The milestone hash.
Returns
async def fetch_current_user_memberships(self, access_token: str, /) -> dict[str, typing.Any]:
1240    async def fetch_current_user_memberships(
1241        self, access_token: str, /
1242    ) -> typedefs.JSONObject:
1243        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1244        resp = await self._request(
1245            RequestMethod.GET,
1246            "User/GetMembershipsForCurrentUser/",
1247            auth=access_token,
1248        )
1249        assert isinstance(resp, dict)
1250        return resp

Fetch a bungie user's accounts with the signed in user. This GET method requires a Bearer access token for the authorization.

This requires OAuth2 scope enabled and the valid Bearer access_token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def equip_item( self, access_token: str, /, item_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> None:
1252    async def equip_item(
1253        self,
1254        access_token: str,
1255        /,
1256        item_id: int,
1257        character_id: int,
1258        membership_type: typedefs.IntAnd[enums.MembershipType],
1259    ) -> None:
1260        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1261        payload = {
1262            "itemId": item_id,
1263            "characterId": character_id,
1264            "membershipType": int(membership_type),
1265        }
1266
1267        await self._request(
1268            RequestMethod.POST,
1269            "Destiny2/Actions/Items/EquipItem/",
1270            json=payload,
1271            auth=access_token,
1272        )

Equip an item to a character.

This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item id.
  • character_id (int): The character's id to equip the item to.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type assocaiated with this player.
async def equip_items( self, access_token: str, /, item_ids: list[int], character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> None:
1274    async def equip_items(
1275        self,
1276        access_token: str,
1277        /,
1278        item_ids: list[int],
1279        character_id: int,
1280        membership_type: typedefs.IntAnd[enums.MembershipType],
1281    ) -> None:
1282        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1283        payload = {
1284            "itemIds": item_ids,
1285            "characterId": character_id,
1286            "membershipType": int(membership_type),
1287        }
1288        await self._request(
1289            RequestMethod.POST,
1290            "Destiny2/Actions/Items/EquipItems/",
1291            json=payload,
1292            auth=access_token,
1293        )

Equip multiple items to a character.

This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_ids (list[int]): A list of item ids.
  • character_id (int): The character's id to equip the item to.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type assocaiated with this player.
async def ban_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], *, length: int = 0, comment: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> None:
1295    async def ban_clan_member(
1296        self,
1297        access_token: str,
1298        /,
1299        group_id: int,
1300        membership_id: int,
1301        membership_type: typedefs.IntAnd[enums.MembershipType],
1302        *,
1303        length: int = 0,
1304        comment: undefined.UndefinedOr[str] = undefined.Undefined,
1305    ) -> None:
1306        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1307        payload = {"comment": str(comment), "length": length}
1308        await self._request(
1309            RequestMethod.POST,
1310            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/",
1311            json=payload,
1312            auth=access_token,
1313        )

Bans a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to ban.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
Other Parameters
  • length (int): An optional ban length.
  • comment (aiobungie.UndefinedOr[str]): An optional comment to this ban. Default is UNDEFINED
async def unban_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> None:
1315    async def unban_clan_member(
1316        self,
1317        access_token: str,
1318        /,
1319        group_id: int,
1320        membership_id: int,
1321        membership_type: typedefs.IntAnd[enums.MembershipType],
1322    ) -> None:
1323        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1324        await self._request(
1325            RequestMethod.POST,
1326            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/",
1327            auth=access_token,
1328        )

Unbans a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to unban.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
async def kick_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
1330    async def kick_clan_member(
1331        self,
1332        access_token: str,
1333        /,
1334        group_id: int,
1335        membership_id: int,
1336        membership_type: typedefs.IntAnd[enums.MembershipType],
1337    ) -> typedefs.JSONObject:
1338        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1339        resp = await self._request(
1340            RequestMethod.POST,
1341            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/",
1342            auth=access_token,
1343        )
1344        assert isinstance(resp, dict)
1345        return resp

Kick a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to kick.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
Returns
async def edit_clan( self, access_token: str, /, group_id: int, *, name: Optional[str] = None, about: Optional[str] = None, motto: Optional[str] = None, theme: Optional[str] = None, tags: Optional[collections.abc.Sequence[str]] = None, is_public: Optional[bool] = None, locale: Optional[str] = None, avatar_image_index: Optional[int] = None, membership_option: Union[NoneType, int, aiobungie.MembershipOption] = None, allow_chat: Optional[bool] = None, chat_security: Optional[Literal[0, 1]] = None, call_sign: Optional[str] = None, homepage: Optional[Literal[0, 1, 2]] = None, enable_invite_messaging_for_admins: Optional[bool] = None, default_publicity: Optional[Literal[0, 1, 2]] = None, is_public_topic_admin: Optional[bool] = None) -> None:
1347    async def edit_clan(
1348        self,
1349        access_token: str,
1350        /,
1351        group_id: int,
1352        *,
1353        name: typedefs.NoneOr[str] = None,
1354        about: typedefs.NoneOr[str] = None,
1355        motto: typedefs.NoneOr[str] = None,
1356        theme: typedefs.NoneOr[str] = None,
1357        tags: typedefs.NoneOr[collections.Sequence[str]] = None,
1358        is_public: typedefs.NoneOr[bool] = None,
1359        locale: typedefs.NoneOr[str] = None,
1360        avatar_image_index: typedefs.NoneOr[int] = None,
1361        membership_option: typedefs.NoneOr[
1362            typedefs.IntAnd[enums.MembershipOption]
1363        ] = None,
1364        allow_chat: typedefs.NoneOr[bool] = None,
1365        chat_security: typedefs.NoneOr[typing.Literal[0, 1]] = None,
1366        call_sign: typedefs.NoneOr[str] = None,
1367        homepage: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1368        enable_invite_messaging_for_admins: typedefs.NoneOr[bool] = None,
1369        default_publicity: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1370        is_public_topic_admin: typedefs.NoneOr[bool] = None,
1371    ) -> None:
1372        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1373        payload = {
1374            "name": name,
1375            "about": about,
1376            "motto": motto,
1377            "theme": theme,
1378            "tags": tags,
1379            "isPublic": is_public,
1380            "avatarImageIndex": avatar_image_index,
1381            "isPublicTopicAdminOnly": is_public_topic_admin,
1382            "allowChat": allow_chat,
1383            "chatSecurity": chat_security,
1384            "callsign": call_sign,
1385            "homepage": homepage,
1386            "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins,
1387            "defaultPublicity": default_publicity,
1388            "locale": locale,
1389        }
1390        if membership_option is not None:
1391            payload["membershipOption"] = int(membership_option)
1392
1393        await self._request(
1394            RequestMethod.POST,
1395            f"GroupV2/{group_id}/Edit",
1396            json=payload,
1397            auth=access_token,
1398        )

Edit a clan.

Notes
  • This request requires OAuth2: oauth2: AdminGroups scope.
  • All arguments will default to None if not provided. This does not include access_token and group_id
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id to edit.
Other Parameters
async def edit_clan_options( self, access_token: str, /, group_id: int, *, invite_permissions_override: Optional[bool] = None, update_culture_permissionOverride: Optional[bool] = None, host_guided_game_permission_override: Optional[Literal[0, 1, 2]] = None, update_banner_permission_override: Optional[bool] = None, join_level: Union[NoneType, int, aiobungie.ClanMemberType] = None) -> None:
1400    async def edit_clan_options(
1401        self,
1402        access_token: str,
1403        /,
1404        group_id: int,
1405        *,
1406        invite_permissions_override: typedefs.NoneOr[bool] = None,
1407        update_culture_permissionOverride: typedefs.NoneOr[bool] = None,
1408        host_guided_game_permission_override: typedefs.NoneOr[
1409            typing.Literal[0, 1, 2]
1410        ] = None,
1411        update_banner_permission_override: typedefs.NoneOr[bool] = None,
1412        join_level: typedefs.NoneOr[typedefs.IntAnd[enums.ClanMemberType]] = None,
1413    ) -> None:
1414
1415        payload = {
1416            "InvitePermissionOverride": invite_permissions_override,
1417            "UpdateCulturePermissionOverride": update_culture_permissionOverride,
1418            "HostGuidedGamePermissionOverride": host_guided_game_permission_override,
1419            "UpdateBannerPermissionOverride": update_banner_permission_override,
1420            "JoinLevel": int(join_level) if join_level else None,
1421        }
1422
1423        await self._request(
1424            RequestMethod.POST,
1425            f"GroupV2/{group_id}/EditFounderOptions",
1426            json=payload,
1427            auth=access_token,
1428        )

Edit the clan options.

Notes
  • This request requires OAuth2: oauth2: AdminGroups scope.
  • All arguments will default to None if not provided. This does not include access_token and group_id
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
Other Parameters
  • invite_permissions_override (aiobungie.typedefs.NoneOr[bool]): Minimum Member Level allowed to invite new members to group Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • update_culture_permissionOverride (aiobungie.typedefs.NoneOr[bool]): Minimum Member Level allowed to update group culture Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • host_guided_game_permission_override (aiobungie.typedefs.NoneOr[typing.Literal[0, 1, 2]]): Minimum Member Level allowed to host guided games Always Allowed: Founder, Acting Founder, Admin Allowed Overrides: 0 -> None, 1 -> Beginner 2 -> Member. Default is Member for clans, None for groups, although this means nothing for groups.
  • update_banner_permission_override (aiobungie.typedefs.NoneOr[bool]): Minimum Member Level allowed to update banner Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • join_level (aiobungie.ClanMemberType): Level to join a member at when accepting an invite, application, or joining an open clan. Default is aiobungie.ClanMemberType.BEGINNER
async def fetch_friends(self, access_token: str, /) -> dict[str, typing.Any]:
1430    async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject:
1431        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1432        resp = await self._request(
1433            RequestMethod.GET,
1434            "Social/Friends/",
1435            auth=access_token,
1436        )
1437        assert isinstance(resp, dict)
1438        return resp

Fetch bungie friend list.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_friend_requests(self, access_token: str, /) -> dict[str, typing.Any]:
1440    async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject:
1441        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1442        resp = await self._request(
1443            RequestMethod.GET,
1444            "Social/Friends/Requests",
1445            auth=access_token,
1446        )
1447        assert isinstance(resp, dict)
1448        return resp

Fetch pending bungie friend requests queue.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1450    async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1451        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1452        await self._request(
1453            RequestMethod.POST,
1454            f"Social/Friends/Requests/Accept/{member_id}",
1455            auth=access_token,
1456        )

Accepts a friend relationship with the target user. The user must be on your incoming friend request list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to accept.
async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1458    async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1459        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1460        await self._request(
1461            RequestMethod.POST,
1462            f"Social/Friends/Add/{member_id}",
1463            auth=access_token,
1464        )

Requests a friend relationship with the target user.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to send the request to.
async def decline_friend_request(self, access_token: str, /, member_id: int) -> None:
1466    async def decline_friend_request(
1467        self, access_token: str, /, member_id: int
1468    ) -> None:
1469        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1470        await self._request(
1471            RequestMethod.POST,
1472            f"Social/Friends/Requests/Decline/{member_id}",
1473            auth=access_token,
1474        )

Decline a friend request with the target user. The user must be in your incoming friend request list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to decline.
async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1476    async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1477        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1478        await self._request(
1479            RequestMethod.POST,
1480            f"Social/Friends/Remove/{member_id}",
1481            auth=access_token,
1482        )

Removes a friend from your friend list. The user must be in your friend list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to remove.
async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1484    async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1485        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1486        await self._request(
1487            RequestMethod.POST,
1488            f"Social/Friends/Requests/Remove/{member_id}",
1489            auth=access_token,
1490        )

Removes a friend from your friend list requests. The user must be in your outgoing request list.

.. note : This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to remove from the requested friend list.
async def approve_all_pending_group_users( self, access_token: str, /, group_id: int, message: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> None:
1492    async def approve_all_pending_group_users(
1493        self,
1494        access_token: str,
1495        /,
1496        group_id: int,
1497        message: undefined.UndefinedOr[str] = undefined.Undefined,
1498    ) -> None:
1499        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1500        await self._request(
1501            RequestMethod.POST,
1502            f"GroupV2/{group_id}/Members/ApproveAll",
1503            auth=access_token,
1504            json={"message": str(message)},
1505        )

Apporve all pending users for the given group id.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other Parameters
  • message (aiobungie.UndefinedOr[str]): An optional message to send with the request. Default is UNDEFINED.
async def deny_all_pending_group_users( self, access_token: str, /, group_id: int, *, message: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> None:
1507    async def deny_all_pending_group_users(
1508        self,
1509        access_token: str,
1510        /,
1511        group_id: int,
1512        *,
1513        message: undefined.UndefinedOr[str] = undefined.Undefined,
1514    ) -> None:
1515        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1516        await self._request(
1517            RequestMethod.POST,
1518            f"GroupV2/{group_id}/Members/DenyAll",
1519            auth=access_token,
1520            json={"message": str(message)},
1521        )

Deny all pending users for the given group id.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other Parameters
  • message (aiobungie.UndefinedOr[str]): An optional message to send with the request. Default is UNDEFINED.
async def add_optional_conversation( self, access_token: str, /, group_id: int, *, name: Union[aiobungie.UndefinedType, str] = UNDEFINED, security: Literal[0, 1] = 0) -> None:
1523    async def add_optional_conversation(
1524        self,
1525        access_token: str,
1526        /,
1527        group_id: int,
1528        *,
1529        name: undefined.UndefinedOr[str] = undefined.Undefined,
1530        security: typing.Literal[0, 1] = 0,
1531    ) -> None:
1532        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1533        payload = {"chatName": str(name), "chatSecurity": security}
1534        await self._request(
1535            RequestMethod.POST,
1536            f"GroupV2/{group_id}/OptionalConversations/Add",
1537            json=payload,
1538            auth=access_token,
1539        )

Add a new chat channel to a group.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other parameters

name: aiobungie.UndefinedOr[str] The chat name. Default to UNDEFINED security: typing.Literal[0, 1] The security level of the chat.

If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`
async def edit_optional_conversation( self, access_token: str, /, group_id: int, conversation_id: int, *, name: Union[aiobungie.UndefinedType, str] = UNDEFINED, security: Literal[0, 1] = 0, enable_chat: bool = False) -> None:
1541    async def edit_optional_conversation(
1542        self,
1543        access_token: str,
1544        /,
1545        group_id: int,
1546        conversation_id: int,
1547        *,
1548        name: undefined.UndefinedOr[str] = undefined.Undefined,
1549        security: typing.Literal[0, 1] = 0,
1550        enable_chat: bool = False,
1551    ) -> None:
1552        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1553        payload = {
1554            "chatEnabled": enable_chat,
1555            "chatName": str(name),
1556            "chatSecurity": security,
1557        }
1558        await self._request(
1559            RequestMethod.POST,
1560            f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}",
1561            json=payload,
1562            auth=access_token,
1563        )

Edit the settings of this chat channel.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
  • conversation_id (int): The conversation/chat id.
Other parameters

name: aiobungie.UndefinedOr[str] The new chat name. Default to UNDEFINED security: typing.Literal[0, 1] The new security level of the chat.

If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`

enable_chat : bool Whether to enable chatting or not. If set to True then chatting will be enabled. Otherwise it will be disabled.

async def transfer_item( self, access_token: str, /, item_id: int, item_hash: int, character_id: int, member_type: Union[int, aiobungie.MembershipType], *, stack_size: int = 1, vault: bool = False) -> None:
1565    async def transfer_item(
1566        self,
1567        access_token: str,
1568        /,
1569        item_id: int,
1570        item_hash: int,
1571        character_id: int,
1572        member_type: typedefs.IntAnd[enums.MembershipType],
1573        *,
1574        stack_size: int = 1,
1575        vault: bool = False,
1576    ) -> None:
1577        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1578        payload = {
1579            "characterId": character_id,
1580            "membershipType": int(member_type),
1581            "itemId": item_id,
1582            "itemReferenceHash": item_hash,
1583            "stackSize": stack_size,
1584            "transferToVault": vault,
1585        }
1586        await self._request(
1587            RequestMethod.POST,
1588            "Destiny2/Actions/Items/TransferItem",
1589            json=payload,
1590            auth=access_token,
1591        )

Transfer an item from / to your vault.

Notes
  • This method requires OAuth2: MoveEquipDestinyItems scope.
  • This method requires both item id and hash.
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item instance id you to transfer.
  • item_hash (int): The item hash.
  • character_id (int): The character id to transfer the item from/to.
  • member_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The user membership type.
Other Parameters
  • stack_size (int): The item stack size.
  • valut (bool): Whether to trasnfer this item to your valut or not. Defaults to False.
async def pull_item( self, access_token: str, /, item_id: int, item_hash: int, character_id: int, member_type: Union[int, aiobungie.MembershipType], *, stack_size: int = 1, vault: bool = False) -> None:
1593    async def pull_item(
1594        self,
1595        access_token: str,
1596        /,
1597        item_id: int,
1598        item_hash: int,
1599        character_id: int,
1600        member_type: typedefs.IntAnd[enums.MembershipType],
1601        *,
1602        stack_size: int = 1,
1603        vault: bool = False,
1604    ) -> None:
1605        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1606        payload = {
1607            "characterId": character_id,
1608            "membershipType": int(member_type),
1609            "itemId": item_id,
1610            "itemReferenceHash": item_hash,
1611            "stackSize": stack_size,
1612            "transferToVault": vault,
1613        }
1614        await self._request(
1615            RequestMethod.POST,
1616            "Destiny2/Actions/Items/PullFromPostmaster",
1617            json=payload,
1618            auth=access_token,
1619        )

pull an item from the postmaster.

Notes
  • This method requires OAuth2: MoveEquipDestinyItems scope.
  • This method requires both item id and hash.
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item instance id to pull.
  • item_hash (int): The item hash.
  • character_id (int): The character id to pull the item to.
  • member_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The user membership type.
Other Parameters
  • stack_size (int): The item stack size.
  • valut (bool): Whether to pill this item to your valut or not. Defaults to False.
async def fetch_fireteams( self, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform] = <FireteamPlatform.ANY: 0>, language: Union[aiobungie.FireteamLanguage, str] = <FireteamLanguage.ALL: >, date_range: Union[int, aiobungie.FireteamDate] = <FireteamDate.ALL: 0>, page: int = 0, slots_filter: int = 0) -> dict[str, typing.Any]:
1621    async def fetch_fireteams(
1622        self,
1623        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1624        *,
1625        platform: typedefs.IntAnd[
1626            fireteams.FireteamPlatform
1627        ] = fireteams.FireteamPlatform.ANY,
1628        language: typing.Union[
1629            fireteams.FireteamLanguage, str
1630        ] = fireteams.FireteamLanguage.ALL,
1631        date_range: typedefs.IntAnd[
1632            fireteams.FireteamDate
1633        ] = fireteams.FireteamDate.ALL,
1634        page: int = 0,
1635        slots_filter: int = 0,
1636    ) -> typedefs.JSONObject:
1637        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1638        resp = await self._request(
1639            RequestMethod.GET,
1640            f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}",  # noqa: E501 Line too long
1641        )
1642        assert isinstance(resp, dict)
1643        return resp

Fetch public Bungie fireteams with open slots.

Parameters
Other Parameters
Returns
async def fetch_avaliable_clan_fireteams( self, access_token: str, group_id: int, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], date_range: Union[int, aiobungie.FireteamDate] = <FireteamDate.ALL: 0>, page: int = 0, public_only: bool = False, slots_filter: int = 0) -> dict[str, typing.Any]:
1645    async def fetch_avaliable_clan_fireteams(
1646        self,
1647        access_token: str,
1648        group_id: int,
1649        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1650        *,
1651        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1652        language: typing.Union[fireteams.FireteamLanguage, str],
1653        date_range: typedefs.IntAnd[
1654            fireteams.FireteamDate
1655        ] = fireteams.FireteamDate.ALL,
1656        page: int = 0,
1657        public_only: bool = False,
1658        slots_filter: int = 0,
1659    ) -> typedefs.JSONObject:
1660        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1661        resp = await self._request(
1662            RequestMethod.GET,
1663            f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}",  # noqa: E501
1664            json={"langFilter": str(language)},
1665            auth=access_token,
1666        )
1667        assert isinstance(resp, dict)
1668        return resp

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id of the fireteam.
  • activity_type (aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]): The fireteam activity type.
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (aiobungie.typedefs.IntAnd[aiobungie.FireteamDate]): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • public_only (bool): If set to True, Then only public fireteams will be returned.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_clan_fireteam( self, access_token: str, fireteam_id: int, group_id: int) -> dict[str, typing.Any]:
1670    async def fetch_clan_fireteam(
1671        self, access_token: str, fireteam_id: int, group_id: int
1672    ) -> typedefs.JSONObject:
1673        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1674        resp = await self._request(
1675            RequestMethod.GET,
1676            f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}",
1677            auth=access_token,
1678        )
1679        assert isinstance(resp, dict)
1680        return resp

Fetch a specific clan fireteam.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch the fireteam from.
  • fireteam_id (int): The fireteam id to fetch.
Returns
async def fetch_my_clan_fireteams( self, access_token: str, group_id: int, *, include_closed: bool = True, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], filtered: bool = True, page: int = 0) -> dict[str, typing.Any]:
1682    async def fetch_my_clan_fireteams(
1683        self,
1684        access_token: str,
1685        group_id: int,
1686        *,
1687        include_closed: bool = True,
1688        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1689        language: typing.Union[fireteams.FireteamLanguage, str],
1690        filtered: bool = True,
1691        page: int = 0,
1692    ) -> typedefs.JSONObject:
1693        payload = {"groupFilter": filtered, "langFilter": str(language)}
1694        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1695        resp = await self._request(
1696            RequestMethod.GET,
1697            f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}",
1698            json=payload,
1699            auth=access_token,
1700        )
1701        assert isinstance(resp, dict)
1702        return resp

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch.
Other Parameters
  • include_closed (bool): If provided and set to True, It will also return closed fireteams. If provided and set to False, It will only return public fireteams. Default is True.
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • filtered (bool): If set to True, it will filter by clan. Otherwise not. Default is True.
  • page (int): The page number. By default its 0 which returns all available activities.
Returns
async def fetch_private_clan_fireteams(self, access_token: str, group_id: int, /) -> int:
1704    async def fetch_private_clan_fireteams(
1705        self, access_token: str, group_id: int, /
1706    ) -> int:
1707        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1708        resp = await self._request(
1709            RequestMethod.GET,
1710            f"Fireteam/Clan/{group_id}/ActiveCount",
1711            auth=access_token,
1712        )
1713        assert isinstance(resp, int)
1714        return resp

Fetch the active count of the clan fireteams that are only private.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id.
Returns
  • int: The active fireteams count. Max value returned is 25.
async def fetch_post_activity(self, instance_id: int, /) -> dict[str, typing.Any]:
1716    async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject:
1717        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1718        resp = await self._request(
1719            RequestMethod.GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}"
1720        )
1721        assert isinstance(resp, dict)
1722        return resp

Fetch a post activity details.

Parameters
  • instance_id (int): The activity instance id.
Returns
async def search_entities( self, name: str, entity_type: str, *, page: int = 0) -> dict[str, typing.Any]:
1724    async def search_entities(
1725        self, name: str, entity_type: str, *, page: int = 0
1726    ) -> typedefs.JSONObject:
1727        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1728        resp = await self._request(
1729            RequestMethod.GET,
1730            f"Destiny2/Armory/Search/{entity_type}/{name}/",
1731            json={"page": page},
1732        )
1733        assert isinstance(resp, dict)
1734        return resp

Search for Destiny2 entities given a name and its type.

Parameters
  • name (str): The name of the entity, i.e., Thunderlord, One thousand voices.
  • entity_type (str): The type of the entity, AKA Definition, For an example DestinyInventoryItemDefinition
Other Parameters
  • page (int): An optional page to return. Default to 0.
Returns
async def fetch_unique_weapon_history( self, membership_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
1736    async def fetch_unique_weapon_history(
1737        self,
1738        membership_id: int,
1739        character_id: int,
1740        membership_type: typedefs.IntAnd[enums.MembershipType],
1741    ) -> typedefs.JSONObject:
1742        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1743        resp = await self._request(
1744            RequestMethod.GET,
1745            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/",
1746        )
1747        assert isinstance(resp, dict)
1748        return resp

Fetch details about unique weapon usage for a character. Includes all exotics.

Parameters
Returns
async def fetch_item( self, member_id: int, item_id: int, membership_type: Union[int, aiobungie.MembershipType], components: list[aiobungie.ComponentType]) -> dict[str, typing.Any]:
1750    async def fetch_item(
1751        self,
1752        member_id: int,
1753        item_id: int,
1754        membership_type: typedefs.IntAnd[enums.MembershipType],
1755        components: list[enums.ComponentType],
1756    ) -> typedefs.JSONObject:
1757        collector = _collect_components(components)
1758        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1759        resp = await self._request(
1760            RequestMethod.GET,
1761            f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}",
1762        )
1763        assert isinstance(resp, dict)
1764        return resp

Fetch an instanced Destiny 2 item's details.

Parameters
Returns
async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> dict[str, typing.Any]:
1766    async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject:
1767        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1768        resp = await self._request(
1769            RequestMethod.GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/"
1770        )
1771        assert isinstance(resp, dict)
1772        return resp

Fetch the weekly reward state for a clan.

Parameters
  • clan_id (int): The clan id.
Returns
async def fetch_available_locales(self) -> dict[str, typing.Any]:
1774    async def fetch_available_locales(self) -> typedefs.JSONObject:
1775        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1776        resp = await self._request(
1777            RequestMethod.GET, "Destiny2/Manifest/DestinyLocaleDefinition/"
1778        )
1779        assert isinstance(resp, dict)
1780        return resp

Fetch available locales at Bungie.

Returns
async def fetch_common_settings(self) -> dict[str, typing.Any]:
1782    async def fetch_common_settings(self) -> typedefs.JSONObject:
1783        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1784        resp = await self._request(RequestMethod.GET, "Settings")
1785        assert isinstance(resp, dict)
1786        return resp

Fetch the common settings used by Bungie's envirotment.

Returns
async def fetch_user_systems_overrides(self) -> dict[str, typing.Any]:
1788    async def fetch_user_systems_overrides(self) -> typedefs.JSONObject:
1789        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1790        resp = await self._request(RequestMethod.GET, "UserSystemOverrides")
1791        assert isinstance(resp, dict)
1792        return resp

Fetch a user's specific system overrides.

Returns
async def fetch_global_alerts(self, *, include_streaming: bool = False) -> list[typing.Any]:
1794    async def fetch_global_alerts(
1795        self, *, include_streaming: bool = False
1796    ) -> typedefs.JSONArray:
1797        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1798        resp = await self._request(
1799            RequestMethod.GET, f"GlobalAlerts/?includestreaming={include_streaming}"
1800        )
1801        assert isinstance(resp, list)
1802        return resp

Fetch any active global alerts.

Parameters
  • include_streaming (bool): If True, the returned results will include streaming alerts. Default is False.
Returns
async def awainitialize_request( self, access_token: str, type: Literal[0, 1], membership_type: Union[int, aiobungie.MembershipType], /, *, affected_item_id: Optional[int] = None, character_id: Optional[int] = None) -> dict[str, typing.Any]:
1804    async def awainitialize_request(
1805        self,
1806        access_token: str,
1807        type: typing.Literal[0, 1],
1808        membership_type: typedefs.IntAnd[enums.MembershipType],
1809        /,
1810        *,
1811        affected_item_id: typing.Optional[int] = None,
1812        character_id: typing.Optional[int] = None,
1813    ) -> typedefs.JSONObject:
1814        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1815
1816        body = {"type": type, "membershipType": int(membership_type)}
1817
1818        if affected_item_id is not None:
1819            body["affectedItemId"] = affected_item_id
1820
1821        if character_id is not None:
1822            body["characterId"] = character_id
1823
1824        resp = await self._request(
1825            RequestMethod.POST, "Destiny2/Awa/Initialize", json=body, auth=access_token
1826        )
1827        assert isinstance(resp, dict)
1828        return resp

Initialize a request to perform an advanced write action.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • type (typing.Literal[0, 1]): Type of the advanced write action. Its either 0 or 1. If set to 0 that means it None. Otherwise if 1 that means its insert plugs.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The Destiny membership type of the account to modify.
Other Parameters
  • affected_item_id (typing.Optional[int]): Item instance ID the action shall be applied to. This is optional for all but a new AwaType values.
  • character_id (typing.Optional[int]): The Destiny character ID to perform this action on.
Returns
async def awaget_action_token(self, access_token: str, correlation_id: str, /) -> dict[str, typing.Any]:
1830    async def awaget_action_token(
1831        self, access_token: str, correlation_id: str, /
1832    ) -> typedefs.JSONObject:
1833        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1834        resp = await self._request(
1835            RequestMethod.POST,
1836            f"Destiny2/Awa/GetActionToken/{correlation_id}",
1837            auth=access_token,
1838        )
1839        assert isinstance(resp, dict)
1840        return resp

Returns the action token if user approves the request.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • correlation_id (str): The identifier for the advanced write action request.
Returns
async def awa_provide_authorization_result( self, access_token: str, selection: int, correlation_id: str, nonce: collections.abc.MutableSequence[typing.Union[str, bytes]]) -> int:
1842    async def awa_provide_authorization_result(
1843        self,
1844        access_token: str,
1845        selection: int,
1846        correlation_id: str,
1847        nonce: collections.MutableSequence[typing.Union[str, bytes]],
1848    ) -> int:
1849        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1850
1851        body = {"selection": selection, "correlationId": correlation_id, "nonce": nonce}
1852
1853        resp = await self._request(
1854            RequestMethod.POST,
1855            "Destiny2/Awa/AwaProvideAuthorizationResult",
1856            json=body,
1857            auth=access_token,
1858        )
1859        assert isinstance(resp, int)
1860        return resp

Provide the result of the user interaction. Called by the Bungie Destiny App to approve or reject a request.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • selection (int): Indication of the selection the user has made (Approving or rejecting the action)
  • correlation_id (str): Correlation ID of the request.
  • nonce (collections.MutableSequence[str, bytes]): Secret nonce received via the PUSH notification.
Returns
  • int: ...
async def fetch_vendors( self, access_token: str, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], /, components: list[aiobungie.ComponentType], filter: Optional[int] = None) -> dict[str, typing.Any]:
1862    async def fetch_vendors(
1863        self,
1864        access_token: str,
1865        character_id: int,
1866        membership_id: int,
1867        membership_type: typedefs.IntAnd[enums.MembershipType],
1868        /,
1869        components: list[enums.ComponentType],
1870        filter: typing.Optional[int] = None,
1871    ) -> typedefs.JSONObject:
1872        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1873        components_ = _collect_components(components)
1874        route = (
1875            f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1876            f"/Character/{character_id}/Vendors/?components={components_}"
1877        )
1878
1879        if filter is not None:
1880            route = route + f"&filter={filter}"
1881
1882        resp = await self._request(
1883            RequestMethod.GET,
1884            route,
1885            auth=access_token,
1886        )
1887        assert isinstance(resp, dict)
1888        return resp

Get currently available vendors from the list of vendors that can possibly have rotating inventory.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • character_id (int): The character ID to return the vendor info for.
  • membership_id (int): The Destiny membership id to return the vendor info for.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The Destiny membership type to return the vendor info for.
  • components (list[aiobungie.ComponentType]): A list of vendor components to collect and return.
Other Parameters
  • filter (int): Filters the type of items returned from the vendor. This can be left to None.
Returns
async def fetch_vendor( self, access_token: str, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], vendor_hash: int, /, components: list[aiobungie.ComponentType]) -> dict[str, typing.Any]:
1890    async def fetch_vendor(
1891        self,
1892        access_token: str,
1893        character_id: int,
1894        membership_id: int,
1895        membership_type: typedefs.IntAnd[enums.MembershipType],
1896        vendor_hash: int,
1897        /,
1898        components: list[enums.ComponentType],
1899    ) -> typedefs.JSONObject:
1900        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1901        components_ = _collect_components(components)
1902        resp = await self._request(
1903            RequestMethod.GET,
1904            (
1905                f"Platform/Destiny2/{int(membership_type)}/Profile/{membership_id}"
1906                f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}"
1907            ),
1908            auth=access_token,
1909        )
1910        assert isinstance(resp, dict)
1911        return resp

Fetch details for a specific vendor.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • character_id (int): The character ID to return the vendor info for.
  • membership_id (int): The Destiny membership id to return the vendor info for.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The Destiny membership type to return the vendor info for.
  • vendor_hash (int): The vendor hash to return the details for.
  • components (list[aiobungie.ComponentType]): A list of vendor components to collect and return.
Returns
async def fetch_application_api_usage( self, access_token: str, application_id: int, /, *, start: Optional[datetime.datetime] = None, end: Optional[datetime.datetime] = None) -> dict[str, typing.Any]:
1913    async def fetch_application_api_usage(
1914        self,
1915        access_token: str,
1916        application_id: int,
1917        /,
1918        *,
1919        start: typing.Optional[datetime.datetime] = None,
1920        end: typing.Optional[datetime.datetime] = None,
1921    ) -> typedefs.JSONObject:
1922
1923        end_date, start_date = time.parse_date_range(end, start)
1924        resp = await self._request(
1925            RequestMethod.GET,
1926            f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}",
1927            auth=access_token,
1928        )
1929        assert isinstance(resp, dict)
1930        return resp

Fetch a Bungie application's API usage.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • application_id (int): The application id to get.
Other Parameters
  • start (typing.Optional[datetime.datetime]): A datetime object can be used to collect the start of the application usage. This is limited and can go back to 30 days maximum.

    If this is left to None. It will return the last 24 hours.

  • end (typing.Optional[datetime.datetime]): A datetime object can be used to collect the end of the application usage.

    If this is left to None. It will return now.

Example
import datetime

# Fetch data from 2021 Dec 10th to 2021 Dec 20th
await fetch_application_api_usage(
    start=datetime.datetime(2021, 12, 10), end=datetime.datetime(2021, 12, 20)
)
Returns
async def fetch_bungie_applications(self) -> list[typing.Any]:
1932    async def fetch_bungie_applications(self) -> typedefs.JSONArray:
1933        resp = await self._request(RequestMethod.GET, "App/FirstParty")
1934        assert isinstance(resp, list)
1935        return resp

Fetch details for applications created by Bungie.

Returns
async def fetch_content_type(self, type: str, /) -> dict[str, typing.Any]:
1937    async def fetch_content_type(self, type: str, /) -> typedefs.JSONObject:
1938        resp = await self._request(RequestMethod.GET, f"Content/GetContentType/{type}/")
1939        assert isinstance(resp, dict)
1940        return resp
async def fetch_content_by_id( self, id: int, locale: str, /, *, head: bool = False) -> dict[str, typing.Any]:
1942    async def fetch_content_by_id(
1943        self, id: int, locale: str, /, *, head: bool = False
1944    ) -> typedefs.JSONObject:
1945        resp = await self._request(
1946            RequestMethod.GET,
1947            f"Content/GetContentById/{id}/{locale}/",
1948            json={"head": head},
1949        )
1950        assert isinstance(resp, dict)
1951        return resp
async def fetch_content_by_tag_and_type( self, locale: str, tag: str, type: str, *, head: bool = False) -> dict[str, typing.Any]:
1953    async def fetch_content_by_tag_and_type(
1954        self, locale: str, tag: str, type: str, *, head: bool = False
1955    ) -> typedefs.JSONObject:
1956        resp = await self._request(
1957            RequestMethod.GET,
1958            f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/",
1959            json={"head": head},
1960        )
1961        assert isinstance(resp, dict)
1962        return resp
async def search_content_with_text( self, locale: str, /, content_type: str, search_text: str, tag: str, *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED, source: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> dict[str, typing.Any]:
1964    async def search_content_with_text(
1965        self,
1966        locale: str,
1967        /,
1968        content_type: str,
1969        search_text: str,
1970        tag: str,
1971        *,
1972        page: undefined.UndefinedOr[int] = undefined.Undefined,
1973        source: undefined.UndefinedOr[str] = undefined.Undefined,
1974    ) -> typedefs.JSONObject:
1975
1976        body: typedefs.JSONObject = {}
1977
1978        body["ctype"] = content_type
1979        body["searchtext"] = search_text
1980        body["tag"] = tag
1981
1982        if page is not undefined.Undefined:
1983            body["currentpage"] = page
1984        else:
1985            body["currentpage"] = 1
1986
1987        if source is not undefined.Undefined:
1988            body["source"] = source
1989        else:
1990            source = ""
1991        resp = await self._request(
1992            RequestMethod.GET, f"Content/Search/{locale}/", json=body
1993        )
1994        assert isinstance(resp, dict)
1995        return resp
async def search_content_by_tag_and_type( self, locale: str, tag: str, type: str, *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED) -> dict[str, typing.Any]:
1997    async def search_content_by_tag_and_type(
1998        self,
1999        locale: str,
2000        tag: str,
2001        type: str,
2002        *,
2003        page: undefined.UndefinedOr[int] = undefined.Undefined,
2004    ) -> typedefs.JSONObject:
2005        body: typedefs.JSONObject = {}
2006        body["currentpage"] = 1 if page is undefined.Undefined else page
2007        resp = await self._request(
2008            RequestMethod.GET,
2009            f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/",
2010            json=body,
2011        )
2012        assert isinstance(resp, dict)
2013        return resp
async def search_help_articles(self, text: str, size: str, /) -> dict[str, typing.Any]:
2015    async def search_help_articles(
2016        self, text: str, size: str, /
2017    ) -> typedefs.JSONObject:
2018        resp = await self._request(
2019            RequestMethod.GET, f"Content/SearchHelpArticles/{text}/{size}/"
2020        )
2021        assert isinstance(resp, dict)
2022        return resp
async def fetch_topics_page( self, category_filter: int, group: int, date_filter: int, sort: Union[str, bytes], *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED, locales: Union[aiobungie.UndefinedType, collections.abc.Iterable[str]] = UNDEFINED, tag_filter: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> dict[str, typing.Any]:
2024    async def fetch_topics_page(
2025        self,
2026        category_filter: int,
2027        group: int,
2028        date_filter: int,
2029        sort: typing.Union[str, bytes],
2030        *,
2031        page: undefined.UndefinedOr[int] = undefined.Undefined,
2032        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.Undefined,
2033        tag_filter: undefined.UndefinedOr[str] = undefined.Undefined,
2034    ) -> typedefs.JSONObject:
2035
2036        body: typedefs.JSONObject = {}
2037        if locales is not undefined.Undefined:
2038            body["locales"] = ",".join(str(locales))
2039        else:
2040            body["locales"] = ",".join([])
2041
2042        if tag_filter is not undefined.Undefined:
2043            body["tagstring"] = tag_filter
2044        else:
2045            body["tagstring"] = ""
2046
2047        page = 0 if page is not undefined.Undefined else page
2048
2049        resp = await self._request(
2050            RequestMethod.GET,
2051            f"Forum/GetTopicsPaged/{page}/{0}/{group}/{sort!s}/{date_filter}/{category_filter}/",
2052            json=body,
2053        )
2054        assert isinstance(resp, dict)
2055        return resp
async def fetch_core_topics_page( self, category_filter: int, date_filter: int, sort: Union[str, bytes], *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED, locales: Union[aiobungie.UndefinedType, collections.abc.Iterable[str]] = UNDEFINED) -> dict[str, typing.Any]:
2057    async def fetch_core_topics_page(
2058        self,
2059        category_filter: int,
2060        date_filter: int,
2061        sort: typing.Union[str, bytes],
2062        *,
2063        page: undefined.UndefinedOr[int] = undefined.Undefined,
2064        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.Undefined,
2065    ) -> typedefs.JSONObject:
2066        body: typedefs.JSONObject = {}
2067
2068        if locales is not undefined.Undefined:
2069            body["locales"] = ",".join(str(locales))
2070        else:
2071            body["locales"] = ",".join([])
2072
2073        resp = await self._request(
2074            RequestMethod.GET,
2075            f"Forum/GetCoreTopicsPaged/{0 if page is undefined.Undefined else page}"
2076            f"/{sort!s}/{date_filter}/{category_filter}/",
2077            json=body,
2078        )
2079        assert isinstance(resp, dict)
2080        return resp
async def fetch_posts_threaded_page( self, parent_post: bool, page: int, page_size: int, parent_post_id: int, reply_size: int, root_thread_mode: bool, sort_mode: int, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2082    async def fetch_posts_threaded_page(
2083        self,
2084        parent_post: bool,
2085        page: int,
2086        page_size: int,
2087        parent_post_id: int,
2088        reply_size: int,
2089        root_thread_mode: bool,
2090        sort_mode: int,
2091        show_banned: typing.Optional[str] = None,
2092    ) -> typedefs.JSONObject:
2093        resp = await self._request(
2094            RequestMethod.GET,
2095            f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/"
2096            f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/",
2097            json={"showbanned": show_banned},
2098        )
2099        assert isinstance(resp, dict)
2100        return resp
async def fetch_posts_threaded_page_from_child( self, child_id: bool, page: int, page_size: int, reply_size: int, root_thread_mode: bool, sort_mode: int, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2102    async def fetch_posts_threaded_page_from_child(
2103        self,
2104        child_id: bool,
2105        page: int,
2106        page_size: int,
2107        reply_size: int,
2108        root_thread_mode: bool,
2109        sort_mode: int,
2110        show_banned: typing.Optional[str] = None,
2111    ) -> typedefs.JSONObject:
2112        resp = await self._request(
2113            RequestMethod.GET,
2114            f"Forum/GetPostsThreadedPagedFromChild/{child_id}/"
2115            f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/",
2116            json={"showbanned": show_banned},
2117        )
2118        assert isinstance(resp, dict)
2119        return resp
async def fetch_post_and_parent( self, child_id: int, /, *, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2121    async def fetch_post_and_parent(
2122        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2123    ) -> typedefs.JSONObject:
2124        resp = await self._request(
2125            RequestMethod.GET,
2126            f"Forum/GetPostAndParent/{child_id}/",
2127            json={"showbanned": show_banned},
2128        )
2129        assert isinstance(resp, dict)
2130        return resp
async def fetch_posts_and_parent_awaiting( self, child_id: int, /, *, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2132    async def fetch_posts_and_parent_awaiting(
2133        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2134    ) -> typedefs.JSONObject:
2135        resp = await self._request(
2136            RequestMethod.GET,
2137            f"Forum/GetPostAndParentAwaitingApproval/{child_id}/",
2138            json={"showbanned": show_banned},
2139        )
2140        assert isinstance(resp, dict)
2141        return resp
async def fetch_topic_for_content(self, content_id: int, /) -> int:
2143    async def fetch_topic_for_content(self, content_id: int, /) -> int:
2144        resp = await self._request(
2145            RequestMethod.GET, f"Forum/GetTopicForContent/{content_id}/"
2146        )
2147        assert isinstance(resp, int)
2148        return resp
async def fetch_forum_tag_suggestions(self, partial_tag: str, /) -> dict[str, typing.Any]:
2150    async def fetch_forum_tag_suggestions(
2151        self, partial_tag: str, /
2152    ) -> typedefs.JSONObject:
2153        resp = await self._request(
2154            RequestMethod.GET,
2155            "Forum/GetForumTagSuggestions/",
2156            json={"partialtag": partial_tag},
2157        )
2158        assert isinstance(resp, dict)
2159        return resp
async def fetch_poll(self, topic_id: int, /) -> dict[str, typing.Any]:
2161    async def fetch_poll(self, topic_id: int, /) -> typedefs.JSONObject:
2162        resp = await self._request(RequestMethod.GET, f"Forum/Poll/{topic_id}/")
2163        assert isinstance(resp, dict)
2164        return resp
async def fetch_recuirement_thread_summaries(self) -> list[typing.Any]:
2166    async def fetch_recuirement_thread_summaries(self) -> typedefs.JSONArray:
2167        resp = await self._request(RequestMethod.POST, "Forum/Recruit/Summaries/")
2168        assert isinstance(resp, list)
2169        return resp
async def fetch_available_avatars(self) -> collections.abc.Mapping[str, int]:
2187    async def fetch_available_avatars(self) -> collections.Mapping[str, int]:
2188        resp = await self._request(RequestMethod.GET, "GroupV2/GetAvailableAvatars/")
2189        assert isinstance(resp, dict)
2190        return resp
async def fetch_user_clan_invite_setting( self, access_token: str, /, membership_type: Union[int, aiobungie.MembershipType]) -> bool:
2192    async def fetch_user_clan_invite_setting(
2193        self,
2194        access_token: str,
2195        /,
2196        membership_type: typedefs.IntAnd[enums.MembershipType],
2197    ) -> bool:
2198        resp = await self._request(
2199            RequestMethod.GET,
2200            f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/",
2201            auth=access_token,
2202        )
2203        assert isinstance(resp, bool)
2204        return resp
async def fetch_banned_group_members( self, access_token: str, group_id: int, /, *, page: int = 1) -> dict[str, typing.Any]:
2206    async def fetch_banned_group_members(
2207        self, access_token: str, group_id: int, /, *, page: int = 1
2208    ) -> typedefs.JSONObject:
2209        resp = await self._request(
2210            RequestMethod.GET,
2211            f"GroupV2/{group_id}/Banned/?currentpage={page}",
2212            auth=access_token,
2213        )
2214        assert isinstance(resp, dict)
2215        return resp
async def fetch_pending_group_memberships( self, access_token: str, group_id: int, /, *, current_page: int = 1) -> dict[str, typing.Any]:
2217    async def fetch_pending_group_memberships(
2218        self, access_token: str, group_id: int, /, *, current_page: int = 1
2219    ) -> typedefs.JSONObject:
2220        resp = await self._request(
2221            RequestMethod.GET,
2222            f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}",
2223            auth=access_token,
2224        )
2225        assert isinstance(resp, dict)
2226        return resp
async def fetch_invited_group_memberships( self, access_token: str, group_id: int, /, *, current_page: int = 1) -> dict[str, typing.Any]:
2228    async def fetch_invited_group_memberships(
2229        self, access_token: str, group_id: int, /, *, current_page: int = 1
2230    ) -> typedefs.JSONObject:
2231        resp = await self._request(
2232            RequestMethod.GET,
2233            f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}",
2234            auth=access_token,
2235        )
2236        assert isinstance(resp, dict)
2237        return resp
async def invite_member_to_group( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], *, message: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> dict[str, typing.Any]:
2239    async def invite_member_to_group(
2240        self,
2241        access_token: str,
2242        /,
2243        group_id: int,
2244        membership_id: int,
2245        membership_type: typedefs.IntAnd[enums.MembershipType],
2246        *,
2247        message: undefined.UndefinedOr[str] = undefined.Undefined,
2248    ) -> typedefs.JSONObject:
2249        resp = await self._request(
2250            RequestMethod.POST,
2251            f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/",
2252            auth=access_token,
2253            json={"message": str(message)},
2254        )
2255        assert isinstance(resp, dict)
2256        return resp
async def cancel_group_member_invite( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
2258    async def cancel_group_member_invite(
2259        self,
2260        access_token: str,
2261        /,
2262        group_id: int,
2263        membership_id: int,
2264        membership_type: typedefs.IntAnd[enums.MembershipType],
2265    ) -> typedefs.JSONObject:
2266        resp = await self._request(
2267            RequestMethod.POST,
2268            f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/",
2269            auth=access_token,
2270        )
2271        assert isinstance(resp, dict)
2272        return resp
async def fetch_historical_definition(self) -> dict[str, typing.Any]:
2274    async def fetch_historical_definition(self) -> typedefs.JSONObject:
2275        resp = await self._request(RequestMethod.GET, "Destiny2/Stats/Definition/")
2276        assert isinstance(resp, dict)
2277        return resp
async def fetch_historical_stats( self, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], day_start: datetime.datetime, day_end: datetime.datetime, groups: list[typing.Union[int, aiobungie.internal.enums.StatsGroupType]], modes: collections.abc.Sequence[typing.Union[int, aiobungie.GameMode]], *, period_type: aiobungie.internal.enums.PeriodType = <PeriodType.ALL_TIME: 2>) -> dict[str, typing.Any]:
2279    async def fetch_historical_stats(
2280        self,
2281        character_id: int,
2282        membership_id: int,
2283        membership_type: typedefs.IntAnd[enums.MembershipType],
2284        day_start: datetime.datetime,
2285        day_end: datetime.datetime,
2286        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2287        modes: collections.Sequence[typedefs.IntAnd[enums.GameMode]],
2288        *,
2289        period_type: enums.PeriodType = enums.PeriodType.ALL_TIME,
2290    ) -> typedefs.JSONObject:
2291
2292        end, start = time.parse_date_range(day_end, day_start)
2293        resp = await self._request(
2294            RequestMethod.GET,
2295            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/",
2296            json={
2297                "dayend": end,
2298                "daystart": start,
2299                "groups": [str(int(group)) for group in groups],
2300                "modes": [str(int(mode)) for mode in modes],
2301                "periodType": int(period_type),
2302            },
2303        )
2304        assert isinstance(resp, dict)
2305        return resp

Fetch historical stats for a specific membership character.

Parameters
  • character_id (int): The character ID to return the stats for.
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
  • day_start (datetime.datetime): The start of the day to return the stats for.
  • day_end (datetime.datetime): The end of the day to return the stats for.
  • groups (list[aiobungie.StatsGroupType]): A list of stats groups to return.
  • modes (list[aiobungie.GameMode | int]): A list of game modes to return.
  • period_type (aiobungie.enums.PeriodType): The period type to return the stats for. This will return ALL_TIME by default if not modified.
Returns
async def fetch_historical_stats_for_account( self, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], groups: list[typing.Union[int, aiobungie.internal.enums.StatsGroupType]]) -> dict[str, typing.Any]:
2307    async def fetch_historical_stats_for_account(
2308        self,
2309        membership_id: int,
2310        membership_type: typedefs.IntAnd[enums.MembershipType],
2311        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2312    ) -> typedefs.JSONObject:
2313        resp = await self._request(
2314            RequestMethod.GET,
2315            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/",
2316            json={"groups": [str(int(group)) for group in groups]},
2317        )
2318        assert isinstance(resp, dict)
2319        return resp

Fetch historical stats for an account's membership.

Parameters
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
  • groups (list[aiobungie.StatsGroupType]): A list of stats groups to return.
Returns
async def fetch_aggregated_activity_stats( self, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], /) -> dict[str, typing.Any]:
2321    async def fetch_aggregated_activity_stats(
2322        self,
2323        character_id: int,
2324        membership_id: int,
2325        membership_type: typedefs.IntAnd[enums.MembershipType],
2326        /,
2327    ) -> typedefs.JSONObject:
2328        resp = await self._request(
2329            RequestMethod.GET,
2330            f"Destiny2/{int(membership_type)}/Account/{membership_id}/"
2331            f"Character/{character_id}/Stats/AggregateActivityStats/",
2332        )
2333        assert isinstance(resp, dict)
2334        return resp

Fetch aggregated activity stats for a specific membership character.

Parameters
  • character_id (int): The character ID to return the stats for.
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
Returns
class RESTPool:
232class RESTPool:
233    """Pool of `RESTClient` instances.
234
235    This allows to create multiple instances of `RESTClient`s that can be acquired
236    which share the same connector and metadata.
237
238    Example
239    -------
240    ```py
241    import aiobungie
242    import asyncio
243
244    client_pool = aiobungie.RESTPool("token", client_id=1234, client_secret='secret')
245
246    # Using a context manager to acquire an instance
247    # of the pool and close the connection after finishing.
248
249    async def first() -> str:
250        async with client_pool.acquire() as client:
251            return client.build_oauth2_url()
252
253    async def second() -> None:
254        async with client_pool.acquire() as client:
255            new_tokens = await client.refresh_access_token("token")
256            client.metadata['tokens'] = new_tokens
257
258    # Client instances are independent from first and second.
259    await asyncio.gather(first(), second())
260    ```
261
262    Parameters
263    ----------
264    token : `str`
265        A valid application token from Bungie's developer portal.
266
267    Other Parameters
268    ----------------
269    max_retries : `int`
270        The max retries number to retry if the request hit a `5xx` status code.
271    max_ratelimit_retries : `int`
272        The max retries number to retry if the request hit a `429` status code. Defaults to `3`.
273    client_secret : `typing.Optional[str]`
274        An optional application client secret,
275        This is only needed if you're fetching OAuth2 tokens with this client.
276    client_id : `typing.Optional[int]`
277        An optional application client id,
278        This is only needed if you're fetching OAuth2 tokens with this client.
279    enable_debugging : `bool | str`
280        Whether to enable logging responses or not.
281
282    Logging Levels
283    --------------
284    * `False`: This will disable logging.
285    * `True`: This will set the level to `DEBUG` and enable logging minimal information.
286    Like the response status, route, taken time and so on.
287    * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
288    """
289
290    __slots__ = (
291        "_token",
292        "_max_retries",
293        "_client_secret",
294        "_client_id",
295        "_max_rate_limit_retries",
296        "_metadata",
297        "_enable_debug",
298    )
299
300    # Looks like mypy doesn't like this.
301    if typing.TYPE_CHECKING:
302        _enable_debug: typing.Union[typing.Literal["TRACE"], bool, int]
303
304    def __init__(
305        self,
306        token: str,
307        /,
308        client_secret: typing.Optional[str] = None,
309        client_id: typing.Optional[int] = None,
310        *,
311        max_retries: int = 4,
312        max_rate_limit_retries: int = 3,
313        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
314    ) -> None:
315        self._client_secret = client_secret
316        self._client_id = client_id
317        self._token: str = token
318        self._max_retries = max_retries
319        self._max_rate_limit_retries = max_rate_limit_retries
320        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
321        self._enable_debug = enable_debugging
322
323    @property
324    def client_id(self) -> typing.Optional[int]:
325        return self._client_id
326
327    @property
328    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
329        """Pool's Metadata. This is different from client instance metadata."""
330        return self._metadata
331
332    @typing.final
333    def acquire(self) -> RESTClient:
334        """Acquires a new `RESTClient` instance from this REST pool.
335
336        Returns
337        -------
338        `RESTClient`
339            An instance of a REST client.
340        """
341        instance = RESTClient(
342            self._token,
343            client_secret=self._client_secret,
344            client_id=self._client_id,
345            max_retries=self._max_retries,
346            max_ratelimit_retries=self._max_rate_limit_retries,
347            enable_debugging=self._enable_debug,
348        )
349        return instance

Pool of RESTClient instances.

This allows to create multiple instances of RESTClients that can be acquired which share the same connector and metadata.

Example
import aiobungie
import asyncio

client_pool = aiobungie.RESTPool("token", client_id=1234, client_secret='secret')

# Using a context manager to acquire an instance
# of the pool and close the connection after finishing.

async def first() -> str:
    async with client_pool.acquire() as client:
        return client.build_oauth2_url()

async def second() -> None:
    async with client_pool.acquire() as client:
        new_tokens = await client.refresh_access_token("token")
        client.metadata['tokens'] = new_tokens

# Client instances are independent from first and second.
await asyncio.gather(first(), second())
Parameters
  • token (str): A valid application token from Bungie's developer portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • max_ratelimit_retries (int): The max retries number to retry if the request hit a 429 status code. Defaults to 3.
  • client_secret (typing.Optional[str]): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (typing.Optional[int]): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
  • enable_debugging (bool | str): Whether to enable logging responses or not.
Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information. Like the response status, route, taken time and so on.
  • "TRACE" | aiobungie.TRACE: This will log the response headers along with the minimal information.
RESTPool( token: str, /, client_secret: Optional[str] = None, client_id: Optional[int] = None, *, max_retries: int = 4, max_rate_limit_retries: int = 3, enable_debugging: Union[Literal['TRACE'], bool, int] = False)
304    def __init__(
305        self,
306        token: str,
307        /,
308        client_secret: typing.Optional[str] = None,
309        client_id: typing.Optional[int] = None,
310        *,
311        max_retries: int = 4,
312        max_rate_limit_retries: int = 3,
313        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
314    ) -> None:
315        self._client_secret = client_secret
316        self._client_id = client_id
317        self._token: str = token
318        self._max_retries = max_retries
319        self._max_rate_limit_retries = max_rate_limit_retries
320        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
321        self._enable_debug = enable_debugging
client_id: Optional[int]
metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

Pool's Metadata. This is different from client instance metadata.

@typing.final
def acquire(self) -> aiobungie.RESTClient:
332    @typing.final
333    def acquire(self) -> RESTClient:
334        """Acquires a new `RESTClient` instance from this REST pool.
335
336        Returns
337        -------
338        `RESTClient`
339            An instance of a REST client.
340        """
341        instance = RESTClient(
342            self._token,
343            client_secret=self._client_secret,
344            client_id=self._client_id,
345            max_retries=self._max_retries,
346            max_ratelimit_retries=self._max_rate_limit_retries,
347            enable_debugging=self._enable_debug,
348        )
349        return instance

Acquires a new RESTClient instance from this REST pool.

Returns
@typing.final
class Race(builtins.int, aiobungie.Enum):
496@typing.final
497class Race(int, Enum):
498    """An Enum for Destiny races."""
499
500    HUMAN = 0
501    AWOKEN = 1
502    EXO = 2
503    UNKNOWN = 3

An Enum for Destiny races.

HUMAN = <Race.HUMAN: 0>
AWOKEN = <Race.AWOKEN: 1>
EXO = <Race.EXO: 2>
UNKNOWN = <Race.UNKNOWN: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Raid(builtins.int, aiobungie.Enum):
148@typing.final
149class Raid(int, Enum):
150    """An Enum for all available raids in Destiny 2."""
151
152    DSC = 910380154
153    """Deep Stone Crypt"""
154
155    LW = 2122313384
156    """Last Wish"""
157
158    VOG = 3881495763
159    """Normal Valut of Glass"""
160
161    GOS = 3458480158
162    """Garden Of Salvation"""

An Enum for all available raids in Destiny 2.

DSC = <Raid.DSC: 910380154>

Deep Stone Crypt

LW = <Raid.LW: 2122313384>

Last Wish

VOG = <Raid.VOG: 3881495763>

Normal Valut of Glass

GOS = <Raid.GOS: 3458480158>

Garden Of Salvation

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class RateLimitedError(aiobungie.HTTPError):
200@attrs.define(auto_exc=True)
201class RateLimitedError(HTTPError):
202    """Raised when being hit with ratelimits."""
203
204    http_status: http.HTTPStatus = attrs.field(
205        default=http.HTTPStatus.TOO_MANY_REQUESTS, init=False
206    )
207    """The request response http status."""
208
209    url: typedefs.StrOrURL
210    """The URL/endpoint caused this error."""
211
212    body: typing.Any
213    """The response body."""
214
215    retry_after: float = attrs.field(default=0.0)
216    """The amount of seconds you need to wait before retrying to requests."""
217
218    message: str = attrs.field(init=False)
219    """A Bungie human readable message describes the cause of the error."""
220
221    @message.default  # type: ignore
222    def _(self) -> str:
223        return f"You're ratelimited for {self.retry_after}, Endpoint: {self.url}. Slow down!"
224
225    def __str__(self) -> str:
226        return self.message

Raised when being hit with ratelimits.

RateLimitedError(url: Union[str, yarl.URL], body: Any, retry_after: float = 0.0)
2def __init__(self, url, body, retry_after=attr_dict['retry_after'].default):
3    self.http_status = attr_dict['http_status'].default
4    self.url = url
5    self.body = body
6    self.retry_after = retry_after
7    self.message = __attr_factory_message(self)
8    BaseException.__init__(self, self.url,self.body,self.retry_after)

Method generated by attrs for class RateLimitedError.

http_status: http.HTTPStatus

The request response http status.

url: Union[str, yarl.URL]

The URL/endpoint caused this error.

body: Any

The response body.

retry_after: float

The amount of seconds you need to wait before retrying to requests.

message: str

A Bungie human readable message describes the cause of the error.

Inherited Members
builtins.BaseException
with_traceback
args
@typing.final
class RecordState(aiobungie.Flag):
49@typing.final
50class RecordState(enums.Flag):
51    """An enum for records component states."""
52
53    NONE = 0
54    REDEEMED = 1
55    UNAVAILABLE = 2
56    OBJECTIVE_NOT_COMPLETED = 4
57    OBSCURED = 8
58    INVISIBLE = 16
59    ENTITLEMENT_UNOWNED = 32
60    CAN_EQUIP_TITLE = 64

An enum for records component states.

NONE = <RecordState.NONE: 0>
REDEEMED = <RecordState.REDEEMED: 1>
UNAVAILABLE = <RecordState.UNAVAILABLE: 2>
OBJECTIVE_NOT_COMPLETED = <RecordState.OBJECTIVE_NOT_COMPLETED: 4>
OBSCURED = <RecordState.OBSCURED: 8>
INVISIBLE = <RecordState.INVISIBLE: 16>
ENTITLEMENT_UNOWNED = <RecordState.ENTITLEMENT_UNOWNED: 32>
CAN_EQUIP_TITLE = <RecordState.CAN_EQUIP_TITLE: 64>
Inherited Members
Flag
name
value
@typing.final
class Relationship(builtins.int, aiobungie.Enum):
691@typing.final
692class Relationship(int, Enum):
693    """An enum for bungie friends relationship types."""
694
695    UNKNOWN = 0
696    FRIEND = 1
697    INCOMING_REQUEST = 2
698    OUTGOING_REQUEST = 3

An enum for bungie friends relationship types.

UNKNOWN = <Relationship.UNKNOWN: 0>
FRIEND = <Relationship.FRIEND: 1>
INCOMING_REQUEST = <Relationship.INCOMING_REQUEST: 2>
OUTGOING_REQUEST = <Relationship.OUTGOING_REQUEST: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class RequestMethod(builtins.str, aiobungie.Enum):
213class RequestMethod(str, enums.Enum):
214    """HTTP request methods enum."""
215
216    GET = "GET"
217    """GET methods."""
218    POST = "POST"
219    """POST methods."""
220    PUT = "PUT"
221    """PUT methods."""
222    PATCH = "PATCH"
223    """PATCH methods."""
224    DELETE = "DELETE"
225    """DELETE methods"""

HTTP request methods enum.

GET = <RequestMethod.GET: GET>

GET methods.

POST = <RequestMethod.POST: POST>

POST methods.

PUT = <RequestMethod.PUT: PUT>

PUT methods.

PATCH = <RequestMethod.PATCH: PATCH>

PATCH methods.

DELETE = <RequestMethod.DELETE: DELETE>

DELETE methods

Inherited Members
Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@attrs.define(auto_exc=True)
class ResponseError(aiobungie.HTTPException):
195@attrs.define(auto_exc=True)
196class ResponseError(HTTPException):
197    """Standard HTTP responses exception."""

Standard HTTP responses exception.

ResponseError( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class ResponseError.

Inherited Members
HTTPException
error_code
http_status
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class Stat(builtins.int, aiobungie.Enum):
518@typing.final
519class Stat(int, Enum):
520    """An Enum for Destiny 2 character stats."""
521
522    NONE = 0
523    MOBILITY = 2996146975
524    RESILIENCE = 392767087
525    RECOVERY = 1943323491
526    DISCIPLINE = 1735777505
527    INTELLECT = 144602215
528    STRENGTH = 4244567218
529    LIGHT_POWER = 1935470627

An Enum for Destiny 2 character stats.

NONE = <Stat.NONE: 0>
MOBILITY = <Stat.MOBILITY: 2996146975>
RESILIENCE = <Stat.RESILIENCE: 392767087>
RECOVERY = <Stat.RECOVERY: 1943323491>
DISCIPLINE = <Stat.DISCIPLINE: 1735777505>
INTELLECT = <Stat.INTELLECT: 144602215>
STRENGTH = <Stat.STRENGTH: 4244567218>
LIGHT_POWER = <Stat.LIGHT_POWER: 1935470627>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
TRACE = 5
@typing.final
class TierType(builtins.int, aiobungie.Enum):
633@typing.final
634class TierType(int, Enum):
635    """An enum for a Destiny 2 item tier type."""
636
637    UNKNOWN = 0
638    CURRENCY = 1
639    BASIC = 2
640    COMMON = 3
641    RARE = 4
642    SUPERIOR = 5
643    EXOTIC = 6

An enum for a Destiny 2 item tier type.

UNKNOWN = <TierType.UNKNOWN: 0>
CURRENCY = <TierType.CURRENCY: 1>
BASIC = <TierType.BASIC: 2>
COMMON = <TierType.COMMON: 3>
RARE = <TierType.RARE: 4>
SUPERIOR = <TierType.SUPERIOR: 5>
EXOTIC = <TierType.EXOTIC: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class TransferStatus(aiobungie.Flag):
743@typing.final
744class TransferStatus(Flag):
745    """An enum for items transfer statuses."""
746
747    CAN_TRANSFER = 0
748    """The item can be transferred."""
749    IS_EQUIPPED = 1
750    """You can't transfer since the item is equipped."""
751    NOT_TRASNFERRABLE = 2
752    """This item can not be transferred."""
753    COULD_BE_TRANSFERRED = 4
754    """You can trasnfer the item. But the place you're trying to put it at has no space for it."""

An enum for items transfer statuses.

CAN_TRANSFER = <TransferStatus.CAN_TRANSFER: 0>

The item can be transferred.

IS_EQUIPPED = <TransferStatus.IS_EQUIPPED: 1>

You can't transfer since the item is equipped.

NOT_TRASNFERRABLE = <TransferStatus.NOT_TRASNFERRABLE: 2>

This item can not be transferred.

COULD_BE_TRANSFERRED = <TransferStatus.COULD_BE_TRANSFERRED: 4>

You can trasnfer the item. But the place you're trying to put it at has no space for it.

Inherited Members
Flag
name
value
@attrs.define(auto_exc=True)
class Unauthorized(aiobungie.HTTPException):
138@attrs.define(auto_exc=True)
139class Unauthorized(HTTPException):
140    """Unauthorized access."""
141
142    http_status: http.HTTPStatus = attrs.field(
143        default=http.HTTPStatus.UNAUTHORIZED, init=False
144    )

Unauthorized access.

Unauthorized( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class Unauthorized.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
Undefined = UNDEFINED
UndefinedOr = typing.Union[aiobungie.UndefinedType, +_T]
class UndefinedType:
33class UndefinedType:
34    """An `UNDEFINED` type."""
35
36    __instance: typing.Optional[UndefinedType] = None
37
38    def __bool__(self) -> typing.Literal[False]:
39        return False
40
41    def __int__(self) -> typing.Literal[0]:
42        return 0
43
44    def __repr__(self) -> str:
45        return "UNDEFINED"
46
47    def __str__(self) -> str:
48        return "UNDEFINED"
49
50    def __new__(cls) -> UndefinedType:
51        if cls.__instance is None:
52            o = super().__new__(cls)
53            cls.__instance = o
54        return cls.__instance

An UNDEFINED type.

UndefinedType()
@typing.final
class ValueUIStyle(builtins.int, aiobungie.Enum):
75@typing.final
76class ValueUIStyle(int, enums.Enum):
77    AUTOMATIC = 0
78    FRACTION = 1
79    CHECK_BOX = 2
80    PERCENTAGE = 3
81    DATETIME = 4
82    FRACTION_FLOAT = 5
83    INTEGER = 6
84    TIME_DURATION = 7
85    HIDDEN = 8
86    MULTIPLIER = 9
87    GREEN_PIPS = 10
88    RED_PIPS = 11
89    EXPLICIT_PERCENTAGE = 12
90    RAW_FLOAT = 13
91    LEVEL_AND_REWARD = 14

An enumeration.

AUTOMATIC = <ValueUIStyle.AUTOMATIC: 0>
FRACTION = <ValueUIStyle.FRACTION: 1>
CHECK_BOX = <ValueUIStyle.CHECK_BOX: 2>
PERCENTAGE = <ValueUIStyle.PERCENTAGE: 3>
DATETIME = <ValueUIStyle.DATETIME: 4>
FRACTION_FLOAT = <ValueUIStyle.FRACTION_FLOAT: 5>
INTEGER = <ValueUIStyle.INTEGER: 6>
TIME_DURATION = <ValueUIStyle.TIME_DURATION: 7>
HIDDEN = <ValueUIStyle.HIDDEN: 8>
MULTIPLIER = <ValueUIStyle.MULTIPLIER: 9>
GREEN_PIPS = <ValueUIStyle.GREEN_PIPS: 10>
RED_PIPS = <ValueUIStyle.RED_PIPS: 11>
EXPLICIT_PERCENTAGE = <ValueUIStyle.EXPLICIT_PERCENTAGE: 12>
RAW_FLOAT = <ValueUIStyle.RAW_FLOAT: 13>
LEVEL_AND_REWARD = <ValueUIStyle.LEVEL_AND_REWARD: 14>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Vendor(builtins.int, aiobungie.Enum):
245@typing.final
246class Vendor(int, Enum):
247    """An Enum for all available vendors in Destiny 2."""
248
249    ZAVALA = 69482069
250    XUR = 2190858386
251    BANSHE = 672118013
252    SPIDER = 863940356
253    SHAXX = 3603221665
254    KADI = 529635856
255    """Postmaster exo."""
256    YUNA = 1796504621
257    """Asia servers only."""
258    EVERVERSE = 3361454721
259    AMANDA = 460529231
260    """Amanda holiday"""
261    CROW = 3611983588
262    HAWTHORNE = 3347378076
263    ADA1 = 350061650
264    DRIFTER = 248695599
265    IKORA = 1976548992
266    SAINT = 765357505
267    """Saint-14"""
268    ERIS_MORN = 1616085565
269    SHAW_HAWN = 1816541247
270    """COSMODROME Guy"""
271    VARIKS = 2531198101

An Enum for all available vendors in Destiny 2.

ZAVALA = <Vendor.ZAVALA: 69482069>
XUR = <Vendor.XUR: 2190858386>
BANSHE = <Vendor.BANSHE: 672118013>
SPIDER = <Vendor.SPIDER: 863940356>
SHAXX = <Vendor.SHAXX: 3603221665>
KADI = <Vendor.KADI: 529635856>

Postmaster exo.

YUNA = <Vendor.YUNA: 1796504621>

Asia servers only.

EVERVERSE = <Vendor.EVERVERSE: 3361454721>
AMANDA = <Vendor.AMANDA: 460529231>

Amanda holiday

CROW = <Vendor.CROW: 3611983588>
HAWTHORNE = <Vendor.HAWTHORNE: 3347378076>
ADA1 = <Vendor.ADA1: 350061650>
DRIFTER = <Vendor.DRIFTER: 248695599>
IKORA = <Vendor.IKORA: 1976548992>
SAINT = <Vendor.SAINT: 765357505>

Saint-14

ERIS_MORN = <Vendor.ERIS_MORN: 1616085565>
SHAW_HAWN = <Vendor.SHAW_HAWN: 1816541247>

COSMODROME Guy

VARIKS = <Vendor.VARIKS: 2531198101>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class WeaponType(builtins.int, aiobungie.Enum):
532@typing.final
533class WeaponType(int, Enum):
534    """Enums for The three Destiny Weapon Types"""
535
536    NONE = 0
537    KINETIC = 1498876634
538    ENERGY = 2465295065
539    POWER = 953998645

Enums for The three Destiny Weapon Types

NONE = <WeaponType.NONE: 0>
KINETIC = <WeaponType.KINETIC: 1498876634>
ENERGY = <WeaponType.ENERGY: 2465295065>
POWER = <WeaponType.POWER: 953998645>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
annotations = _Feature((3, 7, 0, 'beta', 1), (3, 11, 0, 'alpha', 0), 16777216)
def into_iter( iterable: collections.abc.Iterable[~Item]) -> aiobungie.FlatIterator[~Item]:
586def into_iter(
587    iterable: collections.Iterable[Item],
588) -> FlatIterator[Item]:
589    """Transform an iterable into an flat iterator.
590
591    Example
592    -------
593    ```py
594    sequence = [1,2,3]
595    for item in aiobungie.into_iter(sequence).reversed():
596        print(item)
597    # 3
598    # 2
599    # 1
600    ```
601
602    Parameters
603    ----------
604    iterable: `typing.Iterable[Item]`
605        The iterable to convert.
606
607    Raises
608    ------
609    `StopIteration`
610        If no elements are left in the iterator.
611    """
612    return FlatIterator(iterable)

Transform an iterable into an flat iterator.

Example
sequence = [1,2,3]
for item in aiobungie.into_iter(sequence).reversed():
    print(item)
# 3
# 2
# 1
Parameters
  • iterable (typing.Iterable[Item]): The iterable to convert.
Raises
  • StopIteration: If no elements are left in the iterator.
async def raise_error( response: aiohttp.client_reqrep.ClientResponse) -> aiobungie.AiobungieError:
229async def raise_error(response: aiohttp.ClientResponse) -> AiobungieError:
230    """Generates and raise exceptions on error responses."""
231
232    # Not a JSON response, raise immediately.
233
234    # Also Bungie sometimes get funky and return HTML instead of JSON when making an authorized
235    # request with a dummy access token. I can't really do anything about this..
236    if response.content_type != "application/json":
237        return HTTPError(
238            f"Expected JSON content but got {response.content_type!s}, {response.real_url!s}",
239            http.HTTPStatus.UNSUPPORTED_MEDIA_TYPE,
240        )
241
242    body = await response.json()
243    message: str = body.get("Message", "UNDEFINED_MESSAGE")
244    error_status: str = body.get("ErrorStatus", "UNDEFINED_ERROR_STATUS")
245    message_data: dict[str, str] = body.get("MessageData", {})
246    throttle_seconds: int = body.get("ThrottleSeconds", 0)
247    error_code: int = body.get("ErrorCode", 0)
248
249    # Standard HTTP status.
250    if response.status == http.HTTPStatus.NOT_FOUND:
251        return NotFound(
252            message=message,
253            error_code=error_code,
254            throttle_seconds=throttle_seconds,
255            url=str(response.real_url),
256            body=body,
257            headers=response.headers,
258            error_status=error_status,
259            message_data=message_data,
260        )
261
262    elif response.status == http.HTTPStatus.FORBIDDEN:
263        return Forbidden(
264            message=message,
265            error_code=error_code,
266            throttle_seconds=throttle_seconds,
267            url=str(response.real_url),
268            body=body,
269            headers=response.headers,
270            error_status=error_status,
271            message_data=message_data,
272        )
273
274    elif response.status == http.HTTPStatus.UNAUTHORIZED:
275        return Unauthorized(
276            message=message,
277            error_code=error_code,
278            throttle_seconds=throttle_seconds,
279            url=str(response.real_url),
280            body=body,
281            headers=response.headers,
282            error_status=error_status,
283            message_data=message_data,
284        )
285
286    elif response.status == http.HTTPStatus.BAD_REQUEST:
287        # Membership needs to be alone.
288        if error_status == "InvalidParameters":
289            return MembershipTypeError(
290                message=message,
291                body=body,
292                headers=response.headers,
293                url=str(response.url),
294                membership_type=message_data["membershipType"],
295                required_membership=message_data["membershipInfo.membershipType"],
296                membership_id=int(message_data["membershipId"]),
297            )
298        return BadRequest(
299            message=message,
300            body=body,
301            headers=response.headers,
302            url=str(response.url),
303        )
304
305    status = http.HTTPStatus(response.status)
306
307    if 400 <= status < 500:
308        return ResponseError(
309            message=message,
310            error_code=error_code,
311            throttle_seconds=throttle_seconds,
312            url=str(response.real_url),
313            body=body,
314            headers=response.headers,
315            error_status=error_status,
316            message_data=message_data,
317            http_status=status,
318        )
319
320    # Need to self handle ~5xx errors
321    elif 500 <= status < 600:
322        # No API key or method requires OAuth2 most likely.
323        if error_status in {
324            "ApiKeyMissingFromRequest",
325            "WebAuthRequired",
326            "ApiInvalidOrExpiredKey",
327            "AuthenticationInvalid",
328            "AuthorizationCodeInvalid",
329        }:
330            return Unauthorized(
331                message=message,
332                error_code=error_code,
333                throttle_seconds=throttle_seconds,
334                url=str(response.real_url),
335                body=body,
336                headers=response.headers,
337                error_status=error_status,
338                message_data=message_data,
339            )
340
341        # Anything contains not found.
342        elif (
343            "NotFound" in error_status or error_status == "UserCannotFindRequestedUser"
344        ):
345            return NotFound(
346                message=message,
347                error_code=error_code,
348                throttle_seconds=throttle_seconds,
349                url=str(response.real_url),
350                body=body,
351                headers=response.headers,
352                error_status=error_status,
353                message_data=message_data,
354            )
355
356        # Other 5xx errors.
357        else:
358            return InternalServerError(
359                message=message,
360                error_code=error_code,
361                throttle_seconds=throttle_seconds,
362                url=str(response.real_url),
363                body=body,
364                headers=response.headers,
365                error_status=error_status,
366                message_data=message_data,
367                http_status=status,
368            )
369    # Something else.
370    else:
371        return HTTPException(
372            message=message,
373            error_code=error_code,
374            throttle_seconds=throttle_seconds,
375            url=str(response.real_url),
376            body=body,
377            headers=response.headers,
378            error_status=error_status,
379            message_data=message_data,
380            http_status=status,
381        )

Generates and raise exceptions on error responses.

def stringify_http_message(headers: 'collections.Mapping[str, str]') -> str:
384def stringify_http_message(headers: collections.Mapping[str, str]) -> str:
385    return (
386        "{ \n"
387        + "\n".join(  # noqa: W503
388            f"{f'   {key}'}: {value}"
389            if key not in ("Authorization", "X-API-KEY")
390            else f"   {key}: HIDDEN_TOKEN"
391            for key, value in headers.items()
392        )
393        + "\n}"  # noqa: W503
394    )